Remove tst_qquicktext::alignments()
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquicktext / tst_qquicktext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <qtest.h>
42 #include <QtTest/QSignalSpy>
43 #include <QTextDocument>
44 #include <QtQml/qqmlengine.h>
45 #include <QtQml/qqmlcomponent.h>
46 #include <QtQuick/private/qquicktext_p.h>
47 #include <private/qquicktext_p_p.h>
48 #include <private/qquickvaluetypes_p.h>
49 #include <QFontMetrics>
50 #include <qmath.h>
51 #include <QtQuick/QQuickView>
52 #include <private/qguiapplication_p.h>
53 #include <limits.h>
54 #include <QtGui/QMouseEvent>
55 #include "../../shared/util.h"
56 #include "testhttpserver.h"
57
58 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
59
60 #define SERVER_PORT 14459
61 #define SERVER_ADDR "http://127.0.0.1:14459"
62
63 Q_DECLARE_METATYPE(QQuickText::TextFormat)
64
65 QT_BEGIN_NAMESPACE
66 extern void qt_setQtEnableTestFont(bool value);
67 QT_END_NAMESPACE
68
69 class tst_qquicktext : public QQmlDataTest
70 {
71     Q_OBJECT
72 public:
73     tst_qquicktext();
74
75 private slots:
76     void text();
77     void width();
78     void wrap();
79     void elide();
80     void multilineElide_data();
81     void multilineElide();
82     void implicitElide_data();
83     void implicitElide();
84     void textFormat();
85
86     void baseUrl();
87     void embeddedImages_data();
88     void embeddedImages();
89
90     void lineCount();
91     void lineHeight();
92
93     // ### these tests may be trivial
94     void horizontalAlignment();
95     void horizontalAlignment_RightToLeft();
96     void verticalAlignment();
97     void hAlignImplicitWidth();
98     void font();
99     void style();
100     void color();
101     void smooth();
102     void renderType();
103
104     // QQuickFontValueType
105     void weight();
106     void underline();
107     void overline();
108     void strikeout();
109     void capitalization();
110     void letterSpacing();
111     void wordSpacing();
112
113     void clickLink_data();
114     void clickLink();
115
116     void implicitSize_data();
117     void implicitSize();
118     void contentSize();
119     void implicitSizeBinding_data();
120     void implicitSizeBinding();
121     void geometryChanged();
122
123     void boundingRect_data();
124     void boundingRect();
125     void clipRect();
126     void lineLaidOut();
127     void lineLaidOutRelayout();
128
129     void imgTagsBaseUrl_data();
130     void imgTagsBaseUrl();
131     void imgTagsAlign_data();
132     void imgTagsAlign();
133     void imgTagsMultipleImages();
134     void imgTagsElide();
135     void imgTagsUpdates();
136     void imgTagsError();
137     void fontSizeMode_data();
138     void fontSizeMode();
139     void fontSizeModeMultiline_data();
140     void fontSizeModeMultiline();
141     void multilengthStrings_data();
142     void multilengthStrings();
143     void fontFormatSizes_data();
144     void fontFormatSizes();
145
146     void baselineOffset_data();
147     void baselineOffset();
148
149     void htmlLists();
150     void htmlLists_data();
151
152 private:
153     QStringList standard;
154     QStringList richText;
155
156     QStringList horizontalAlignmentmentStrings;
157     QStringList verticalAlignmentmentStrings;
158
159     QList<Qt::Alignment> verticalAlignmentments;
160     QList<Qt::Alignment> horizontalAlignmentments;
161
162     QStringList styleStrings;
163     QList<QQuickText::TextStyle> styles;
164
165     QStringList colorStrings;
166
167     QQmlEngine engine;
168
169     QQuickView *createView(const QString &filename);
170     int numberOfNonWhitePixels(int fromX, int toX, const QImage &image);
171 };
172
173 tst_qquicktext::tst_qquicktext()
174 {
175     standard << "the quick brown fox jumped over the lazy dog"
176             << "the quick brown fox\n jumped over the lazy dog";
177
178     richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
179             << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
180
181     horizontalAlignmentmentStrings << "AlignLeft"
182             << "AlignRight"
183             << "AlignHCenter";
184
185     verticalAlignmentmentStrings << "AlignTop"
186             << "AlignBottom"
187             << "AlignVCenter";
188
189     horizontalAlignmentments << Qt::AlignLeft
190             << Qt::AlignRight
191             << Qt::AlignHCenter;
192
193     verticalAlignmentments << Qt::AlignTop
194             << Qt::AlignBottom
195             << Qt::AlignVCenter;
196
197     styleStrings << "Normal"
198             << "Outline"
199             << "Raised"
200             << "Sunken";
201
202     styles << QQuickText::Normal
203             << QQuickText::Outline
204             << QQuickText::Raised
205             << QQuickText::Sunken;
206
207     colorStrings << "aliceblue"
208             << "antiquewhite"
209             << "aqua"
210             << "darkkhaki"
211             << "darkolivegreen"
212             << "dimgray"
213             << "palevioletred"
214             << "lightsteelblue"
215             << "#000000"
216             << "#AAAAAA"
217             << "#FFFFFF"
218             << "#2AC05F";
219     //
220     // need a different test to do alpha channel test
221     // << "#AA0011DD"
222     // << "#00F16B11";
223     //
224     qt_setQtEnableTestFont(true);
225 }
226
227 QQuickView *tst_qquicktext::createView(const QString &filename)
228 {
229     QQuickView *window = new QQuickView(0);
230
231     window->setSource(QUrl::fromLocalFile(filename));
232     return window;
233 }
234
235 void tst_qquicktext::text()
236 {
237     {
238         QQmlComponent textComponent(&engine);
239         textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
240         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
241
242         QVERIFY(textObject != 0);
243         QCOMPARE(textObject->text(), QString(""));
244         QVERIFY(textObject->width() == 0);
245
246         delete textObject;
247     }
248
249     for (int i = 0; i < standard.size(); i++)
250     {
251         QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
252         QQmlComponent textComponent(&engine);
253         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
254
255         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
256
257         QVERIFY(textObject != 0);
258         QCOMPARE(textObject->text(), standard.at(i));
259         QVERIFY(textObject->width() > 0);
260
261         delete textObject;
262     }
263
264     for (int i = 0; i < richText.size(); i++)
265     {
266         QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
267         QQmlComponent textComponent(&engine);
268         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
269         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
270
271         QVERIFY(textObject != 0);
272         QString expected = richText.at(i);
273         QCOMPARE(textObject->text(), expected.replace("\\\"", "\""));
274         QVERIFY(textObject->width() > 0);
275
276         delete textObject;
277     }
278 }
279
280 void tst_qquicktext::width()
281 {
282     // uses Font metrics to find the width for standard and document to find the width for rich
283     {
284         QQmlComponent textComponent(&engine);
285         textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
286         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
287
288         QVERIFY(textObject != 0);
289         QCOMPARE(textObject->width(), 0.);
290
291         delete textObject;
292     }
293
294     bool requiresUnhintedMetrics = !qmlDisableDistanceField();
295
296     for (int i = 0; i < standard.size(); i++)
297     {
298         QVERIFY(!Qt::mightBeRichText(standard.at(i))); // self-test
299
300         QFont f;
301         qreal metricWidth = 0.0;
302
303         if (requiresUnhintedMetrics) {
304             QString s = standard.at(i);
305             s.replace(QLatin1Char('\n'), QChar::LineSeparator);
306
307             QTextLayout layout(s);
308             layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
309             {
310                 QTextOption option;
311                 option.setUseDesignMetrics(true);
312                 layout.setTextOption(option);
313             }
314
315             layout.beginLayout();
316             forever {
317                 QTextLine line = layout.createLine();
318                 if (!line.isValid())
319                     break;
320             }
321
322             layout.endLayout();
323
324             metricWidth = layout.boundingRect().width();
325         } else {
326             QFontMetricsF fm(f);
327             metricWidth = fm.size(Qt::TextExpandTabs && Qt::TextShowMnemonic, standard.at(i)).width();
328         }
329
330         QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
331         QQmlComponent textComponent(&engine);
332         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
333         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
334
335         QVERIFY(textObject != 0);
336         QVERIFY(textObject->boundingRect().width() > 0);
337         QCOMPARE(textObject->width(), qreal(metricWidth));
338         QVERIFY(textObject->textFormat() == QQuickText::AutoText); // setting text doesn't change format
339
340         delete textObject;
341     }
342
343     for (int i = 0; i < richText.size(); i++)
344     {
345         QVERIFY(Qt::mightBeRichText(richText.at(i))); // self-test
346
347         QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\"; textFormat: Text.RichText }";
348         QQmlComponent textComponent(&engine);
349         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
350         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
351         QVERIFY(textObject != 0);
352
353         QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
354         QVERIFY(textPrivate != 0);
355         QVERIFY(textPrivate->extra.isAllocated());
356
357         QTextDocument *doc = textPrivate->extra->doc;
358         QVERIFY(doc != 0);
359
360         QCOMPARE(int(textObject->width()), int(doc->idealWidth()));
361         QVERIFY(textObject->textFormat() == QQuickText::RichText);
362
363         delete textObject;
364     }
365 }
366
367 void tst_qquicktext::wrap()
368 {
369     int textHeight = 0;
370     // for specified width and wrap set true
371     {
372         QQmlComponent textComponent(&engine);
373         textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; wrapMode: Text.WordWrap; width: 300 }", QUrl::fromLocalFile(""));
374         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
375         textHeight = textObject->height();
376
377         QVERIFY(textObject != 0);
378         QVERIFY(textObject->wrapMode() == QQuickText::WordWrap);
379         QCOMPARE(textObject->width(), 300.);
380
381         delete textObject;
382     }
383
384     for (int i = 0; i < standard.size(); i++)
385     {
386         QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + standard.at(i) + "\" }";
387         QQmlComponent textComponent(&engine);
388         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
389         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
390
391         QVERIFY(textObject != 0);
392         QCOMPARE(textObject->width(), 30.);
393         QVERIFY(textObject->height() > textHeight);
394
395         int oldHeight = textObject->height();
396         textObject->setWidth(100);
397         QVERIFY(textObject->height() < oldHeight);
398
399         delete textObject;
400     }
401
402     for (int i = 0; i < richText.size(); i++)
403     {
404         QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + richText.at(i) + "\" }";
405         QQmlComponent textComponent(&engine);
406         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
407         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
408
409         QVERIFY(textObject != 0);
410         QCOMPARE(textObject->width(), 30.);
411         QVERIFY(textObject->height() > textHeight);
412
413         qreal oldHeight = textObject->height();
414         textObject->setWidth(100);
415         QVERIFY(textObject->height() < oldHeight);
416
417         delete textObject;
418     }
419
420     // richtext again with a fixed height
421     for (int i = 0; i < richText.size(); i++)
422     {
423         QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; height: 50; text: \"" + richText.at(i) + "\" }";
424         QQmlComponent textComponent(&engine);
425         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
426         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
427
428         QVERIFY(textObject != 0);
429         QCOMPARE(textObject->width(), 30.);
430         QVERIFY(textObject->implicitHeight() > textHeight);
431
432         qreal oldHeight = textObject->implicitHeight();
433         textObject->setWidth(100);
434         QVERIFY(textObject->implicitHeight() < oldHeight);
435
436         delete textObject;
437     }
438
439     {
440         QQmlComponent component(&engine);
441         component.setData("import QtQuick 2.0\n Text {}", QUrl());
442         QScopedPointer<QObject> object(component.create());
443         QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
444         QVERIFY(textObject);
445
446         QSignalSpy spy(textObject, SIGNAL(wrapModeChanged()));
447
448         QCOMPARE(textObject->wrapMode(), QQuickText::NoWrap);
449
450         textObject->setWrapMode(QQuickText::Wrap);
451         QCOMPARE(textObject->wrapMode(), QQuickText::Wrap);
452         QCOMPARE(spy.count(), 1);
453
454         textObject->setWrapMode(QQuickText::Wrap);
455         QCOMPARE(spy.count(), 1);
456
457         textObject->setWrapMode(QQuickText::NoWrap);
458         QCOMPARE(textObject->wrapMode(), QQuickText::NoWrap);
459         QCOMPARE(spy.count(), 2);
460     }
461 }
462
463 void tst_qquicktext::elide()
464 {
465     for (QQuickText::TextElideMode m = QQuickText::ElideLeft; m<=QQuickText::ElideNone; m=QQuickText::TextElideMode(int(m)+1)) {
466         const char* elidename[]={"ElideLeft", "ElideRight", "ElideMiddle", "ElideNone"};
467         QString elide = "elide: Text." + QString(elidename[int(m)]) + ";";
468
469         // XXX Poor coverage.
470
471         {
472             QQmlComponent textComponent(&engine);
473             textComponent.setData(("import QtQuick 2.0\nText { text: \"\"; "+elide+" width: 100 }").toLatin1(), QUrl::fromLocalFile(""));
474             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
475
476             QCOMPARE(textObject->elideMode(), m);
477             QCOMPARE(textObject->width(), 100.);
478
479             delete textObject;
480         }
481
482         for (int i = 0; i < standard.size(); i++)
483         {
484             QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + standard.at(i) + "\" }";
485             QQmlComponent textComponent(&engine);
486             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
487             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
488
489             QCOMPARE(textObject->elideMode(), m);
490             QCOMPARE(textObject->width(), 100.);
491
492             if (m != QQuickText::ElideNone && !standard.at(i).contains('\n'))
493                 QVERIFY(textObject->contentWidth() <= textObject->width());
494
495             delete textObject;
496         }
497
498         for (int i = 0; i < richText.size(); i++)
499         {
500             QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + richText.at(i) + "\" }";
501             QQmlComponent textComponent(&engine);
502             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
503             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
504
505             QCOMPARE(textObject->elideMode(), m);
506             QCOMPARE(textObject->width(), 100.);
507
508             if (m != QQuickText::ElideNone && standard.at(i).contains("<br>"))
509                 QVERIFY(textObject->contentWidth() <= textObject->width());
510
511             delete textObject;
512         }
513     }
514 }
515
516 void tst_qquicktext::multilineElide_data()
517 {
518     QTest::addColumn<QQuickText::TextFormat>("format");
519     QTest::newRow("plain") << QQuickText::PlainText;
520     QTest::newRow("styled") << QQuickText::StyledText;
521 }
522
523 void tst_qquicktext::multilineElide()
524 {
525     QFETCH(QQuickText::TextFormat, format);
526     QQuickView *window = createView(testFile("multilineelide.qml"));
527
528     QQuickText *myText = qobject_cast<QQuickText*>(window->rootObject());
529     QVERIFY(myText != 0);
530     myText->setTextFormat(format);
531
532     QCOMPARE(myText->lineCount(), 3);
533     QCOMPARE(myText->truncated(), true);
534
535     qreal lineHeight = myText->contentHeight() / 3.;
536
537     // Set a valid height greater than the truncated content height and ensure the line count is
538     // unchanged.
539     myText->setHeight(200);
540     QCOMPARE(myText->lineCount(), 3);
541     QCOMPARE(myText->truncated(), true);
542
543     // reduce size and ensure fewer lines are drawn
544     myText->setHeight(lineHeight * 2);
545     QCOMPARE(myText->lineCount(), 2);
546
547     myText->setHeight(lineHeight);
548     QCOMPARE(myText->lineCount(), 1);
549
550     myText->setHeight(5);
551     QCOMPARE(myText->lineCount(), 1);
552
553     myText->setHeight(lineHeight * 3);
554     QCOMPARE(myText->lineCount(), 3);
555
556     // remove max count and show all lines.
557     myText->setHeight(1000);
558     myText->resetMaximumLineCount();
559
560     QCOMPARE(myText->truncated(), false);
561
562     // reduce size again
563     myText->setHeight(lineHeight * 2);
564     QCOMPARE(myText->lineCount(), 2);
565     QCOMPARE(myText->truncated(), true);
566
567     // change line height
568     myText->setLineHeight(1.1);
569     QCOMPARE(myText->lineCount(), 1);
570
571     delete window;
572 }
573
574 void tst_qquicktext::implicitElide_data()
575 {
576     QTest::addColumn<QString>("width");
577     QTest::addColumn<QString>("initialText");
578     QTest::addColumn<QString>("text");
579
580     QTest::newRow("maximum width, empty")
581             << "Math.min(implicitWidth, 100)"
582             << "";
583     QTest::newRow("maximum width, short")
584             << "Math.min(implicitWidth, 100)"
585             << "the";
586     QTest::newRow("maximum width, long")
587             << "Math.min(implicitWidth, 100)"
588             << "the quick brown fox jumped over the lazy dog";
589     QTest::newRow("reset width, empty")
590             << "implicitWidth > 100 ? 100 : undefined"
591             << "";
592     QTest::newRow("reset width, short")
593             << "implicitWidth > 100 ? 100 : undefined"
594             << "the";
595     QTest::newRow("reset width, long")
596             << "implicitWidth > 100 ? 100 : undefined"
597             << "the quick brown fox jumped over the lazy dog";
598 }
599
600 void tst_qquicktext::implicitElide()
601 {
602     QFETCH(QString, width);
603     QFETCH(QString, initialText);
604
605     QString componentStr =
606             "import QtQuick 2.0\n"
607             "Text {\n"
608                 "width: " + width + "\n"
609                 "text: \"" + initialText + "\"\n"
610                 "elide: Text.ElideRight\n"
611             "}";
612     QQmlComponent textComponent(&engine);
613     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
614     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
615
616     QVERIFY(textObject->contentWidth() <= textObject->width());
617
618     textObject->setText("the quick brown fox jumped over");
619
620     QVERIFY(textObject->contentWidth() > 0);
621     QVERIFY(textObject->contentWidth() <= textObject->width());
622 }
623
624 void tst_qquicktext::textFormat()
625 {
626     {
627         QQmlComponent textComponent(&engine);
628         textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
629         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
630
631         QVERIFY(textObject != 0);
632         QVERIFY(textObject->textFormat() == QQuickText::RichText);
633
634         QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
635         QVERIFY(textPrivate != 0);
636         QVERIFY(textPrivate->richText == true);
637
638         delete textObject;
639     }
640     {
641         QQmlComponent textComponent(&engine);
642         textComponent.setData("import QtQuick 2.0\nText { text: \"<b>Hello</b>\" }", QUrl::fromLocalFile(""));
643         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
644
645         QVERIFY(textObject != 0);
646         QVERIFY(textObject->textFormat() == QQuickText::AutoText);
647
648         QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
649         QVERIFY(textPrivate != 0);
650         QVERIFY(textPrivate->styledText == true);
651
652         delete textObject;
653     }
654     {
655         QQmlComponent textComponent(&engine);
656         textComponent.setData("import QtQuick 2.0\nText { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
657         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
658
659         QVERIFY(textObject != 0);
660         QVERIFY(textObject->textFormat() == QQuickText::PlainText);
661
662         delete textObject;
663     }
664
665     {
666         QQmlComponent component(&engine);
667         component.setData("import QtQuick 2.0\n Text {}", QUrl());
668         QScopedPointer<QObject> object(component.create());
669         QQuickText *text = qobject_cast<QQuickText *>(object.data());
670         QVERIFY(text);
671
672         QSignalSpy spy(text, SIGNAL(textFormatChanged(TextFormat)));
673
674         QCOMPARE(text->textFormat(), QQuickText::AutoText);
675
676         text->setTextFormat(QQuickText::StyledText);
677         QCOMPARE(text->textFormat(), QQuickText::StyledText);
678         QCOMPARE(spy.count(), 1);
679
680         text->setTextFormat(QQuickText::StyledText);
681         QCOMPARE(spy.count(), 1);
682
683         text->setTextFormat(QQuickText::AutoText);
684         QCOMPARE(text->textFormat(), QQuickText::AutoText);
685         QCOMPARE(spy.count(), 2);
686     }
687 }
688
689 //the alignment tests may be trivial o.oa
690 void tst_qquicktext::horizontalAlignment()
691 {
692     //test one align each, and then test if two align fails.
693
694     for (int i = 0; i < standard.size(); i++)
695     {
696         for (int j=0; j < horizontalAlignmentmentStrings.size(); j++)
697         {
698             QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
699             QQmlComponent textComponent(&engine);
700             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
701             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
702
703             QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
704
705             delete textObject;
706         }
707     }
708
709     for (int i = 0; i < richText.size(); i++)
710     {
711         for (int j=0; j < horizontalAlignmentmentStrings.size(); j++)
712         {
713             QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
714             QQmlComponent textComponent(&engine);
715             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
716             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
717
718             QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
719
720             delete textObject;
721         }
722     }
723
724 }
725
726 void tst_qquicktext::horizontalAlignment_RightToLeft()
727 {
728     QQuickView *window = createView(testFile("horizontalAlignment_RightToLeft.qml"));
729     QQuickText *text = window->rootObject()->findChild<QQuickText*>("text");
730     QVERIFY(text != 0);
731     window->show();
732
733     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(text);
734     QVERIFY(textPrivate != 0);
735
736     QTRY_VERIFY(textPrivate->layout.lineCount());
737
738     // implicit alignment should follow the reading direction of RTL text
739     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
740     QCOMPARE(text->effectiveHAlign(), text->hAlign());
741     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
742
743     // explicitly left aligned text
744     text->setHAlign(QQuickText::AlignLeft);
745     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
746     QCOMPARE(text->effectiveHAlign(), text->hAlign());
747     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
748
749     // explicitly right aligned text
750     text->setHAlign(QQuickText::AlignRight);
751     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
752     QCOMPARE(text->effectiveHAlign(), text->hAlign());
753     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
754
755     // change to rich text
756     QString textString = text->text();
757     text->setText(QString("<i>") + textString + QString("</i>"));
758     text->setTextFormat(QQuickText::RichText);
759     text->resetHAlign();
760
761     // implicitly aligned rich text should follow the reading direction of text
762     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
763     QCOMPARE(text->effectiveHAlign(), text->hAlign());
764     QVERIFY(textPrivate->extra.isAllocated());
765     QVERIFY(textPrivate->extra->doc->defaultTextOption().alignment() & Qt::AlignLeft);
766
767     // explicitly left aligned rich text
768     text->setHAlign(QQuickText::AlignLeft);
769     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
770     QCOMPARE(text->effectiveHAlign(), text->hAlign());
771     QVERIFY(textPrivate->extra->doc->defaultTextOption().alignment() & Qt::AlignRight);
772
773     // explicitly right aligned rich text
774     text->setHAlign(QQuickText::AlignRight);
775     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
776     QCOMPARE(text->effectiveHAlign(), text->hAlign());
777     QVERIFY(textPrivate->extra->doc->defaultTextOption().alignment() & Qt::AlignLeft);
778
779     text->setText(textString);
780     text->setTextFormat(QQuickText::PlainText);
781
782     // explicitly center aligned
783     text->setHAlign(QQuickText::AlignHCenter);
784     QCOMPARE(text->hAlign(), QQuickText::AlignHCenter);
785     QCOMPARE(text->effectiveHAlign(), text->hAlign());
786     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
787     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().right() > window->width()/2);
788
789     // reseted alignment should go back to following the text reading direction
790     text->resetHAlign();
791     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
792     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
793
794     // mirror the text item
795     QQuickItemPrivate::get(text)->setLayoutMirror(true);
796
797     // mirrored implicit alignment should continue to follow the reading direction of the text
798     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
799     QCOMPARE(text->effectiveHAlign(), QQuickText::AlignRight);
800     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
801
802     // mirrored explicitly right aligned behaves as left aligned
803     text->setHAlign(QQuickText::AlignRight);
804     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
805     QCOMPARE(text->effectiveHAlign(), QQuickText::AlignLeft);
806     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
807
808     // mirrored explicitly left aligned behaves as right aligned
809     text->setHAlign(QQuickText::AlignLeft);
810     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
811     QCOMPARE(text->effectiveHAlign(), QQuickText::AlignRight);
812     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
813
814     // disable mirroring
815     QQuickItemPrivate::get(text)->setLayoutMirror(false);
816     text->resetHAlign();
817
818     // English text should be implicitly left aligned
819     text->setText("Hello world!");
820     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
821     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
822
823     // empty text with implicit alignment follows the system locale-based
824     // keyboard input direction from QInputMethod::inputDirection()
825     text->setText("");
826     QCOMPARE(text->hAlign(), qApp->inputMethod()->inputDirection() == Qt::LeftToRight ?
827                                   QQuickText::AlignLeft : QQuickText::AlignRight);
828     text->setHAlign(QQuickText::AlignRight);
829     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
830
831     delete window;
832
833     // alignment of Text with no text set to it
834     QString componentStr = "import QtQuick 2.0\nText {}";
835     QQmlComponent textComponent(&engine);
836     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
837     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
838     QCOMPARE(textObject->hAlign(), qApp->inputMethod()->inputDirection() == Qt::LeftToRight ?
839                                   QQuickText::AlignLeft : QQuickText::AlignRight);
840     delete textObject;
841 }
842
843 int tst_qquicktext::numberOfNonWhitePixels(int fromX, int toX, const QImage &image)
844 {
845     int pixels = 0;
846     for (int x = fromX; x < toX; ++x) {
847         for (int y = 0; y < image.height(); ++y) {
848             if (image.pixel(x, y) != qRgb(255, 255, 255))
849                 pixels++;
850         }
851     }
852     return pixels;
853 }
854
855 void tst_qquicktext::hAlignImplicitWidth()
856 {
857     QQuickView view(testFileUrl("hAlignImplicitWidth.qml"));
858     view.show();
859     view.requestActivateWindow();
860     QVERIFY(QTest::qWaitForWindowActive(&view));
861
862     QQuickText *text = view.rootObject()->findChild<QQuickText*>("textItem");
863     QVERIFY(text != 0);
864
865     {
866         // Left Align
867         QImage image = view.grabWindow();
868         int left = numberOfNonWhitePixels(0, image.width() / 3, image);
869         int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
870         int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
871         QVERIFY(left > mid);
872         QVERIFY(mid > right);
873     }
874     {
875         // HCenter Align
876         text->setHAlign(QQuickText::AlignHCenter);
877         QImage image = view.grabWindow();
878         int left = numberOfNonWhitePixels(0, image.width() / 3, image);
879         int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
880         int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
881         QVERIFY(left < mid);
882         QVERIFY(mid > right);
883     }
884     {
885         // Right Align
886         text->setHAlign(QQuickText::AlignRight);
887         QImage image = view.grabWindow();
888         int left = numberOfNonWhitePixels(0, image.width() / 3, image);
889         int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
890         int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
891         QVERIFY(left < mid);
892         QVERIFY(mid < right);
893     }
894 }
895
896 void tst_qquicktext::verticalAlignment()
897 {
898     //test one align each, and then test if two align fails.
899
900     for (int i = 0; i < standard.size(); i++)
901     {
902         for (int j=0; j < verticalAlignmentmentStrings.size(); j++)
903         {
904             QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
905             QQmlComponent textComponent(&engine);
906             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
907             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
908
909             QVERIFY(textObject != 0);
910             QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
911
912             delete textObject;
913         }
914     }
915
916     for (int i = 0; i < richText.size(); i++)
917     {
918         for (int j=0; j < verticalAlignmentmentStrings.size(); j++)
919         {
920             QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
921             QQmlComponent textComponent(&engine);
922             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
923             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
924
925             QVERIFY(textObject != 0);
926             QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
927
928             delete textObject;
929         }
930     }
931
932 }
933
934 void tst_qquicktext::font()
935 {
936     //test size, then bold, then italic, then family
937     {
938         QString componentStr = "import QtQuick 2.0\nText { font.pointSize: 40; text: \"Hello World\" }";
939         QQmlComponent textComponent(&engine);
940         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
941         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
942
943         QCOMPARE(textObject->font().pointSize(), 40);
944         QCOMPARE(textObject->font().bold(), false);
945         QCOMPARE(textObject->font().italic(), false);
946
947         delete textObject;
948     }
949
950     {
951         QString componentStr = "import QtQuick 2.0\nText { font.pixelSize: 40; text: \"Hello World\" }";
952         QQmlComponent textComponent(&engine);
953         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
954         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
955
956         QCOMPARE(textObject->font().pixelSize(), 40);
957         QCOMPARE(textObject->font().bold(), false);
958         QCOMPARE(textObject->font().italic(), false);
959
960         delete textObject;
961     }
962
963     {
964         QString componentStr = "import QtQuick 2.0\nText { font.bold: true; text: \"Hello World\" }";
965         QQmlComponent textComponent(&engine);
966         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
967         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
968
969         QCOMPARE(textObject->font().bold(), true);
970         QCOMPARE(textObject->font().italic(), false);
971
972         delete textObject;
973     }
974
975     {
976         QString componentStr = "import QtQuick 2.0\nText { font.italic: true; text: \"Hello World\" }";
977         QQmlComponent textComponent(&engine);
978         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
979         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
980
981         QCOMPARE(textObject->font().italic(), true);
982         QCOMPARE(textObject->font().bold(), false);
983
984         delete textObject;
985     }
986
987     {
988         QString componentStr = "import QtQuick 2.0\nText { font.family: \"Helvetica\"; text: \"Hello World\" }";
989         QQmlComponent textComponent(&engine);
990         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
991         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
992
993         QCOMPARE(textObject->font().family(), QString("Helvetica"));
994         QCOMPARE(textObject->font().bold(), false);
995         QCOMPARE(textObject->font().italic(), false);
996
997         delete textObject;
998     }
999
1000     {
1001         QString componentStr = "import QtQuick 2.0\nText { font.family: \"\"; text: \"Hello World\" }";
1002         QQmlComponent textComponent(&engine);
1003         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1004         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1005
1006         QCOMPARE(textObject->font().family(), QString(""));
1007
1008         delete textObject;
1009     }
1010 }
1011
1012 void tst_qquicktext::style()
1013 {
1014     //test style
1015     for (int i = 0; i < styles.size(); i++)
1016     {
1017         QString componentStr = "import QtQuick 2.0\nText { style: \"" + styleStrings.at(i) + "\"; styleColor: \"white\"; text: \"Hello World\" }";
1018         QQmlComponent textComponent(&engine);
1019         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1020         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1021
1022         QCOMPARE((int)textObject->style(), (int)styles.at(i));
1023         QCOMPARE(textObject->styleColor(), QColor("white"));
1024
1025         delete textObject;
1026     }
1027     QString componentStr = "import QtQuick 2.0\nText { text: \"Hello World\" }";
1028     QQmlComponent textComponent(&engine);
1029     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1030     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1031
1032     QRectF brPre = textObject->boundingRect();
1033     textObject->setStyle(QQuickText::Outline);
1034     QRectF brPost = textObject->boundingRect();
1035
1036     QVERIFY(brPre.width() < brPost.width());
1037     QVERIFY(brPre.height() < brPost.height());
1038
1039     delete textObject;
1040 }
1041
1042 void tst_qquicktext::color()
1043 {
1044     //test style
1045     for (int i = 0; i < colorStrings.size(); i++)
1046     {
1047         QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1048         QQmlComponent textComponent(&engine);
1049         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1050         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1051
1052         QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
1053         QCOMPARE(textObject->styleColor(), QColor("black"));
1054         QCOMPARE(textObject->linkColor(), QColor("blue"));
1055
1056         delete textObject;
1057     }
1058
1059     for (int i = 0; i < colorStrings.size(); i++)
1060     {
1061         QString componentStr = "import QtQuick 2.0\nText { styleColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1062         QQmlComponent textComponent(&engine);
1063         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1064         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1065
1066         QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(i)));
1067         // default color to black?
1068         QCOMPARE(textObject->color(), QColor("black"));
1069         QCOMPARE(textObject->linkColor(), QColor("blue"));
1070
1071         QSignalSpy colorSpy(textObject, SIGNAL(colorChanged()));
1072         QSignalSpy linkColorSpy(textObject, SIGNAL(linkColorChanged()));
1073
1074         textObject->setColor(QColor("white"));
1075         QCOMPARE(textObject->color(), QColor("white"));
1076         QCOMPARE(colorSpy.count(), 1);
1077
1078         textObject->setLinkColor(QColor("black"));
1079         QCOMPARE(textObject->linkColor(), QColor("black"));
1080         QCOMPARE(linkColorSpy.count(), 1);
1081
1082         textObject->setColor(QColor("white"));
1083         QCOMPARE(colorSpy.count(), 1);
1084
1085         textObject->setLinkColor(QColor("black"));
1086         QCOMPARE(linkColorSpy.count(), 1);
1087
1088         textObject->setColor(QColor("black"));
1089         QCOMPARE(textObject->color(), QColor("black"));
1090         QCOMPARE(colorSpy.count(), 2);
1091
1092         textObject->setLinkColor(QColor("blue"));
1093         QCOMPARE(textObject->linkColor(), QColor("blue"));
1094         QCOMPARE(linkColorSpy.count(), 2);
1095
1096         delete textObject;
1097     }
1098
1099     for (int i = 0; i < colorStrings.size(); i++)
1100     {
1101         QString componentStr = "import QtQuick 2.0\nText { linkColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1102         QQmlComponent textComponent(&engine);
1103         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1104         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1105
1106         QCOMPARE(textObject->styleColor(), QColor("black"));
1107         QCOMPARE(textObject->color(), QColor("black"));
1108         QCOMPARE(textObject->linkColor(), QColor(colorStrings.at(i)));
1109
1110         delete textObject;
1111     }
1112
1113     for (int i = 0; i < colorStrings.size(); i++)
1114     {
1115         for (int j = 0; j < colorStrings.size(); j++)
1116         {
1117             QString componentStr = "import QtQuick 2.0\nText { "
1118                     "color: \"" + colorStrings.at(i) + "\"; "
1119                     "styleColor: \"" + colorStrings.at(j) + "\"; "
1120                     "linkColor: \"" + colorStrings.at(j) + "\"; "
1121                     "text: \"Hello World\" }";
1122             QQmlComponent textComponent(&engine);
1123             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1124             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1125
1126             QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
1127             QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(j)));
1128             QCOMPARE(textObject->linkColor(), QColor(colorStrings.at(j)));
1129
1130             delete textObject;
1131         }
1132     }
1133     {
1134         QString colorStr = "#AA001234";
1135         QColor testColor("#001234");
1136         testColor.setAlpha(170);
1137
1138         QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStr + "\"; text: \"Hello World\" }";
1139         QQmlComponent textComponent(&engine);
1140         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1141         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1142
1143         QCOMPARE(textObject->color(), testColor);
1144
1145         delete textObject;
1146     } {
1147         QString colorStr = "#001234";
1148         QColor testColor(colorStr);
1149
1150         QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStr + "\"; text: \"Hello World\" }";
1151         QQmlComponent textComponent(&engine);
1152         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1153         QScopedPointer<QObject> object(textComponent.create());
1154         QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
1155
1156         QSignalSpy spy(textObject, SIGNAL(colorChanged()));
1157
1158         QCOMPARE(textObject->color(), testColor);
1159         textObject->setColor(testColor);
1160         QCOMPARE(textObject->color(), testColor);
1161         QCOMPARE(spy.count(), 0);
1162
1163         testColor = QColor("black");
1164         textObject->setColor(testColor);
1165         QCOMPARE(textObject->color(), testColor);
1166         QCOMPARE(spy.count(), 1);
1167     } {
1168         QString colorStr = "#001234";
1169         QColor testColor(colorStr);
1170
1171         QString componentStr = "import QtQuick 2.0\nText { styleColor: \"" + colorStr + "\"; text: \"Hello World\" }";
1172         QQmlComponent textComponent(&engine);
1173         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1174         QScopedPointer<QObject> object(textComponent.create());
1175         QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
1176
1177         QSignalSpy spy(textObject, SIGNAL(styleColorChanged()));
1178
1179         QCOMPARE(textObject->styleColor(), testColor);
1180         textObject->setStyleColor(testColor);
1181         QCOMPARE(textObject->styleColor(), testColor);
1182         QCOMPARE(spy.count(), 0);
1183
1184         testColor = QColor("black");
1185         textObject->setStyleColor(testColor);
1186         QCOMPARE(textObject->styleColor(), testColor);
1187         QCOMPARE(spy.count(), 1);
1188     } {
1189         QString colorStr = "#001234";
1190         QColor testColor(colorStr);
1191
1192         QString componentStr = "import QtQuick 2.0\nText { linkColor: \"" + colorStr + "\"; text: \"Hello World\" }";
1193         QQmlComponent textComponent(&engine);
1194         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1195         QScopedPointer<QObject> object(textComponent.create());
1196         QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
1197
1198         QSignalSpy spy(textObject, SIGNAL(linkColorChanged()));
1199
1200         QCOMPARE(textObject->linkColor(), testColor);
1201         textObject->setLinkColor(testColor);
1202         QCOMPARE(textObject->linkColor(), testColor);
1203         QCOMPARE(spy.count(), 0);
1204
1205         testColor = QColor("black");
1206         textObject->setLinkColor(testColor);
1207         QCOMPARE(textObject->linkColor(), testColor);
1208         QCOMPARE(spy.count(), 1);
1209     }
1210 }
1211
1212 void tst_qquicktext::smooth()
1213 {
1214     for (int i = 0; i < standard.size(); i++)
1215     {
1216         {
1217             QString componentStr = "import QtQuick 2.0\nText { smooth: false; text: \"" + standard.at(i) + "\" }";
1218             QQmlComponent textComponent(&engine);
1219             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1220             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1221             QCOMPARE(textObject->smooth(), false);
1222
1223             delete textObject;
1224         }
1225         {
1226             QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
1227             QQmlComponent textComponent(&engine);
1228             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1229             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1230             QCOMPARE(textObject->smooth(), true);
1231
1232             delete textObject;
1233         }
1234     }
1235     for (int i = 0; i < richText.size(); i++)
1236     {
1237         {
1238             QString componentStr = "import QtQuick 2.0\nText { smooth: false; text: \"" + richText.at(i) + "\" }";
1239             QQmlComponent textComponent(&engine);
1240             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1241             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1242             QCOMPARE(textObject->smooth(), false);
1243
1244             delete textObject;
1245         }
1246         {
1247             QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
1248             QQmlComponent textComponent(&engine);
1249             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1250             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1251             QCOMPARE(textObject->smooth(), true);
1252
1253             delete textObject;
1254         }
1255     }
1256 }
1257
1258 void tst_qquicktext::renderType()
1259 {
1260     QQmlComponent component(&engine);
1261     component.setData("import QtQuick 2.0\n Text {}", QUrl());
1262     QScopedPointer<QObject> object(component.create());
1263     QQuickText *text = qobject_cast<QQuickText *>(object.data());
1264     QVERIFY(text);
1265
1266     QSignalSpy spy(text, SIGNAL(renderTypeChanged()));
1267
1268     QCOMPARE(text->renderType(), QQuickText::QtRendering);
1269
1270     text->setRenderType(QQuickText::NativeRendering);
1271     QCOMPARE(text->renderType(), QQuickText::NativeRendering);
1272     QCOMPARE(spy.count(), 1);
1273
1274     text->setRenderType(QQuickText::NativeRendering);
1275     QCOMPARE(spy.count(), 1);
1276
1277     text->setRenderType(QQuickText::QtRendering);
1278     QCOMPARE(text->renderType(), QQuickText::QtRendering);
1279     QCOMPARE(spy.count(), 2);
1280 }
1281
1282 void tst_qquicktext::weight()
1283 {
1284     {
1285         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1286         QQmlComponent textComponent(&engine);
1287         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1288         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1289
1290         QVERIFY(textObject != 0);
1291         QCOMPARE((int)textObject->font().weight(), (int)QQuickFontValueType::Normal);
1292
1293         delete textObject;
1294     }
1295     {
1296         QString componentStr = "import QtQuick 2.0\nText { font.weight: \"Bold\"; text: \"Hello world!\" }";
1297         QQmlComponent textComponent(&engine);
1298         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1299         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1300
1301         QVERIFY(textObject != 0);
1302         QCOMPARE((int)textObject->font().weight(), (int)QQuickFontValueType::Bold);
1303
1304         delete textObject;
1305     }
1306 }
1307
1308 void tst_qquicktext::underline()
1309 {
1310     QQuickView view(testFileUrl("underline.qml"));
1311     view.show();
1312     view.requestActivateWindow();
1313     QVERIFY(QTest::qWaitForWindowActive(&view));
1314     QQuickText *textObject = view.rootObject()->findChild<QQuickText*>("myText");
1315     QVERIFY(textObject != 0);
1316     QCOMPARE(textObject->font().overline(), false);
1317     QCOMPARE(textObject->font().underline(), true);
1318     QCOMPARE(textObject->font().strikeOut(), false);
1319 }
1320
1321 void tst_qquicktext::overline()
1322 {
1323     QQuickView view(testFileUrl("overline.qml"));
1324     view.show();
1325     view.requestActivateWindow();
1326     QVERIFY(QTest::qWaitForWindowActive(&view));
1327     QQuickText *textObject = view.rootObject()->findChild<QQuickText*>("myText");
1328     QVERIFY(textObject != 0);
1329     QCOMPARE(textObject->font().overline(), true);
1330     QCOMPARE(textObject->font().underline(), false);
1331     QCOMPARE(textObject->font().strikeOut(), false);
1332 }
1333
1334 void tst_qquicktext::strikeout()
1335 {
1336     QQuickView view(testFileUrl("strikeout.qml"));
1337     view.show();
1338     view.requestActivateWindow();
1339     QVERIFY(QTest::qWaitForWindowActive(&view));
1340     QQuickText *textObject = view.rootObject()->findChild<QQuickText*>("myText");
1341     QVERIFY(textObject != 0);
1342     QCOMPARE(textObject->font().overline(), false);
1343     QCOMPARE(textObject->font().underline(), false);
1344     QCOMPARE(textObject->font().strikeOut(), true);
1345 }
1346
1347 void tst_qquicktext::capitalization()
1348 {
1349     {
1350         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1351         QQmlComponent textComponent(&engine);
1352         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1353         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1354
1355         QVERIFY(textObject != 0);
1356         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::MixedCase);
1357
1358         delete textObject;
1359     }
1360     {
1361         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllUppercase\" }";
1362         QQmlComponent textComponent(&engine);
1363         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1364         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1365
1366         QVERIFY(textObject != 0);
1367         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::AllUppercase);
1368
1369         delete textObject;
1370     }
1371     {
1372         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllLowercase\" }";
1373         QQmlComponent textComponent(&engine);
1374         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1375         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1376
1377         QVERIFY(textObject != 0);
1378         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::AllLowercase);
1379
1380         delete textObject;
1381     }
1382     {
1383         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"SmallCaps\" }";
1384         QQmlComponent textComponent(&engine);
1385         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1386         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1387
1388         QVERIFY(textObject != 0);
1389         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::SmallCaps);
1390
1391         delete textObject;
1392     }
1393     {
1394         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"Capitalize\" }";
1395         QQmlComponent textComponent(&engine);
1396         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1397         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1398
1399         QVERIFY(textObject != 0);
1400         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::Capitalize);
1401
1402         delete textObject;
1403     }
1404 }
1405
1406 void tst_qquicktext::letterSpacing()
1407 {
1408     {
1409         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1410         QQmlComponent textComponent(&engine);
1411         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1412         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1413
1414         QVERIFY(textObject != 0);
1415         QCOMPARE(textObject->font().letterSpacing(), 0.0);
1416
1417         delete textObject;
1418     }
1419     {
1420         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: -2 }";
1421         QQmlComponent textComponent(&engine);
1422         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1423         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1424
1425         QVERIFY(textObject != 0);
1426         QCOMPARE(textObject->font().letterSpacing(), -2.);
1427
1428         delete textObject;
1429     }
1430     {
1431         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: 3 }";
1432         QQmlComponent textComponent(&engine);
1433         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1434         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1435
1436         QVERIFY(textObject != 0);
1437         QCOMPARE(textObject->font().letterSpacing(), 3.);
1438
1439         delete textObject;
1440     }
1441 }
1442
1443 void tst_qquicktext::wordSpacing()
1444 {
1445     {
1446         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1447         QQmlComponent textComponent(&engine);
1448         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1449         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1450
1451         QVERIFY(textObject != 0);
1452         QCOMPARE(textObject->font().wordSpacing(), 0.0);
1453
1454         delete textObject;
1455     }
1456     {
1457         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: -50 }";
1458         QQmlComponent textComponent(&engine);
1459         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1460         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1461
1462         QVERIFY(textObject != 0);
1463         QCOMPARE(textObject->font().wordSpacing(), -50.);
1464
1465         delete textObject;
1466     }
1467     {
1468         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: 200 }";
1469         QQmlComponent textComponent(&engine);
1470         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1471         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1472
1473         QVERIFY(textObject != 0);
1474         QCOMPARE(textObject->font().wordSpacing(), 200.);
1475
1476         delete textObject;
1477     }
1478 }
1479
1480 class EventSender : public QQuickItem
1481 {
1482 public:
1483     void sendEvent(QMouseEvent *event) {
1484         if (event->type() == QEvent::MouseButtonPress)
1485             mousePressEvent(event);
1486         else if (event->type() == QEvent::MouseButtonRelease)
1487             mouseReleaseEvent(event);
1488         else if (event->type() == QEvent::MouseMove)
1489             mouseMoveEvent(event);
1490         else
1491             qWarning() << "Trying to send unsupported event type";
1492     }
1493 };
1494
1495 class LinkTest : public QObject
1496 {
1497     Q_OBJECT
1498 public:
1499     LinkTest() {}
1500
1501     QString link;
1502
1503 public slots:
1504     void linkClicked(QString l) { link = l; }
1505 };
1506
1507 class TextMetrics
1508 {
1509 public:
1510     TextMetrics(const QString &text, Qt::TextElideMode elideMode = Qt::ElideNone)
1511     {
1512         QString adjustedText = text;
1513         adjustedText.replace(QLatin1Char('\n'), QChar(QChar::LineSeparator));
1514         if (elideMode == Qt::ElideLeft)
1515             adjustedText = QChar(0x2026) + adjustedText;
1516         else if (elideMode == Qt::ElideRight)
1517             adjustedText = adjustedText + QChar(0x2026);
1518
1519         layout.setText(adjustedText);
1520         QTextOption option;
1521         option.setUseDesignMetrics(true);
1522         layout.setTextOption(option);
1523
1524         layout.beginLayout();
1525         qreal height = 0;
1526         QTextLine line = layout.createLine();
1527         while (line.isValid()) {
1528             line.setLineWidth(FLT_MAX);
1529             line.setPosition(QPointF(0, height));
1530             height += line.height();
1531             line = layout.createLine();
1532         }
1533         layout.endLayout();
1534     }
1535
1536     qreal width() const { return layout.maximumWidth(); }
1537
1538     QRectF characterRectangle(
1539             int position,
1540             int hAlign = Qt::AlignLeft,
1541             int vAlign = Qt::AlignTop,
1542             const QSizeF &bounds = QSizeF(240, 320)) const
1543     {
1544         qreal dy = 0;
1545         switch (vAlign) {
1546         case Qt::AlignBottom:
1547             dy = bounds.height() - layout.boundingRect().height();
1548             break;
1549         case Qt::AlignVCenter:
1550             dy = (bounds.height() - layout.boundingRect().height()) / 2;
1551             break;
1552         default:
1553             break;
1554         }
1555
1556         for (int i = 0; i < layout.lineCount(); ++i) {
1557             QTextLine line = layout.lineAt(i);
1558             if (position >= line.textStart() + line.textLength())
1559                 continue;
1560             qreal dx = 0;
1561             switch (hAlign) {
1562             case Qt::AlignRight:
1563                 dx = bounds.width() - line.naturalTextWidth();
1564                 break;
1565             case Qt::AlignHCenter:
1566                 dx = (bounds.width() - line.naturalTextWidth()) / 2;
1567                 break;
1568             default:
1569                 break;
1570             }
1571
1572             QRectF rect;
1573             rect.setLeft(dx + line.cursorToX(position, QTextLine::Leading));
1574             rect.setRight(dx + line.cursorToX(position, QTextLine::Trailing));
1575             rect.setTop(dy + line.y());
1576             rect.setBottom(dy + line.y() + line.height());
1577
1578             return rect;
1579         }
1580         return QRectF();
1581     }
1582
1583     QTextLayout layout;
1584 };
1585
1586
1587 typedef QVector<QPointF> PointVector;
1588 Q_DECLARE_METATYPE(PointVector);
1589
1590 void tst_qquicktext::clickLink_data()
1591 {
1592     QTest::addColumn<QString>("text");
1593     QTest::addColumn<qreal>("width");
1594     QTest::addColumn<QString>("bindings");
1595     QTest::addColumn<PointVector>("mousePositions");
1596     QTest::addColumn<QString>("link");
1597
1598     const QString singleLineText = "this text has a <a href=\\\"http://qt-project.org/single\\\">link</a> in it";
1599     const QString singleLineLink = "http://qt-project.org/single";
1600     const QString multipleLineText = "this text<br/>has <a href=\\\"http://qt-project.org/multiple\\\">multiple<br/>lines</a> in it";
1601     const QString multipleLineLink = "http://qt-project.org/multiple";
1602     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>";
1603     const QString outerLink = "http://qt-project.org/outer";
1604     const QString innerLink = "http://qt-project.org/inner";
1605
1606     {
1607         const TextMetrics metrics("this text has a link in it");
1608
1609         QTest::newRow("click on link")
1610                 << singleLineText << 240.
1611                 << ""
1612                 << (PointVector() << metrics.characterRectangle(18).center())
1613                 << singleLineLink;
1614         QTest::newRow("click on text")
1615                 << singleLineText << 240.
1616                 << ""
1617                 << (PointVector() << metrics.characterRectangle(13).center())
1618                 << QString();
1619         QTest::newRow("drag within link")
1620                 << singleLineText << 240.
1621                 << ""
1622                 << (PointVector()
1623                     << metrics.characterRectangle(17).center()
1624                     << metrics.characterRectangle(19).center())
1625                 << singleLineLink;
1626         QTest::newRow("drag away from link")
1627                 << singleLineText << 240.
1628                 << ""
1629                 << (PointVector()
1630                     << metrics.characterRectangle(18).center()
1631                     << metrics.characterRectangle(13).center())
1632                 << QString();
1633         QTest::newRow("drag on to link")
1634                 << singleLineText << 240.
1635                 << ""
1636                 << (PointVector()
1637                     << metrics.characterRectangle(13).center()
1638                     << metrics.characterRectangle(18).center())
1639                 << QString();
1640         QTest::newRow("click on bottom right aligned link")
1641                 << singleLineText << 240.
1642                 << "horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignBottom"
1643                 << (PointVector() << metrics.characterRectangle(18, Qt::AlignRight, Qt::AlignBottom).center())
1644                 << singleLineLink;
1645         QTest::newRow("click on center aligned link")
1646                 << singleLineText << 240.
1647                 << "horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter"
1648                 << (PointVector() << metrics.characterRectangle(18, Qt::AlignHCenter, Qt::AlignVCenter).center())
1649                 << singleLineLink;
1650         QTest::newRow("click on rich text link")
1651                 << singleLineText << 240.
1652                 << "textFormat: Text.RichText"
1653                 << (PointVector() << metrics.characterRectangle(18).center())
1654                 << singleLineLink;
1655         QTest::newRow("click on rich text")
1656                 << singleLineText << 240.
1657                 << "textFormat: Text.RichText"
1658                 << (PointVector() << metrics.characterRectangle(13).center())
1659                 << QString();
1660         QTest::newRow("click on bottom right aligned rich text link")
1661                 << singleLineText << 240.
1662                 << "textFormat: Text.RichText; horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignBottom"
1663                 << (PointVector() << metrics.characterRectangle(18, Qt::AlignRight, Qt::AlignBottom).center())
1664                 << singleLineLink;
1665         QTest::newRow("click on center aligned rich text link")
1666                 << singleLineText << 240.
1667                 << "textFormat: Text.RichText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter"
1668                 << (PointVector() << metrics.characterRectangle(18, Qt::AlignHCenter, Qt::AlignVCenter).center())
1669                 << singleLineLink;
1670     } {
1671         const TextMetrics metrics("this text has a li", Qt::ElideRight);
1672         QTest::newRow("click on right elided link")
1673                 << singleLineText << metrics.width() +  2
1674                 << "elide: Text.ElideRight"
1675                 << (PointVector() << metrics.characterRectangle(17).center())
1676                 << singleLineLink;
1677     } {
1678         const TextMetrics metrics("ink in it", Qt::ElideLeft);
1679         QTest::newRow("click on left elided link")
1680                 << singleLineText << metrics.width() +  2
1681                 << "elide: Text.ElideLeft"
1682                 << (PointVector() << metrics.characterRectangle(2).center())
1683                 << singleLineLink;
1684     } {
1685         const TextMetrics metrics("this text\nhas multiple\nlines in it");
1686         QTest::newRow("click on second line")
1687                 << multipleLineText << 240.
1688                 << ""
1689                 << (PointVector() << metrics.characterRectangle(18).center())
1690                 << multipleLineLink;
1691         QTest::newRow("click on third line")
1692                 << multipleLineText << 240.
1693                 << ""
1694                 << (PointVector() << metrics.characterRectangle(25).center())
1695                 << multipleLineLink;
1696         QTest::newRow("drag from second line to third")
1697                 << multipleLineText << 240.
1698                 << ""
1699                 << (PointVector()
1700                     << metrics.characterRectangle(18).center()
1701                     << metrics.characterRectangle(25).center())
1702                 << multipleLineLink;
1703         QTest::newRow("click on rich text second line")
1704                 << multipleLineText << 240.
1705                 << "textFormat: Text.RichText"
1706                 << (PointVector() << metrics.characterRectangle(18).center())
1707                 << multipleLineLink;
1708         QTest::newRow("click on rich text third line")
1709                 << multipleLineText << 240.
1710                 << "textFormat: Text.RichText"
1711                 << (PointVector() << metrics.characterRectangle(25).center())
1712                 << multipleLineLink;
1713         QTest::newRow("drag rich text from second line to third")
1714                 << multipleLineText << 240.
1715                 << "textFormat: Text.RichText"
1716                 << (PointVector()
1717                     << metrics.characterRectangle(18).center()
1718                     << metrics.characterRectangle(25).center())
1719                 << multipleLineLink;
1720     } {
1721         const TextMetrics metrics("this text has a nested link in it");
1722         QTest::newRow("click on left outer link")
1723                 << nestedText << 240.
1724                 << ""
1725                 << (PointVector() << metrics.characterRectangle(22).center())
1726                 << outerLink;
1727         QTest::newRow("click on right outer link")
1728                 << nestedText << 240.
1729                 << ""
1730                 << (PointVector() << metrics.characterRectangle(27).center())
1731                 << outerLink;
1732         QTest::newRow("click on inner link left")
1733                 << nestedText << 240.
1734                 << ""
1735                 << (PointVector() << metrics.characterRectangle(23).center())
1736                 << innerLink;
1737         QTest::newRow("click on inner link right")
1738                 << nestedText << 240.
1739                 << ""
1740                 << (PointVector() << metrics.characterRectangle(26).center())
1741                 << innerLink;
1742         QTest::newRow("drag from inner to outer link")
1743                 << nestedText << 240.
1744                 << ""
1745                 << (PointVector()
1746                     << metrics.characterRectangle(25).center()
1747                     << metrics.characterRectangle(30).center())
1748                 << QString();
1749         QTest::newRow("drag from outer to inner link")
1750                 << nestedText << 240.
1751                 << ""
1752                 << (PointVector()
1753                     << metrics.characterRectangle(30).center()
1754                     << metrics.characterRectangle(25).center())
1755                 << QString();
1756         QTest::newRow("click on left outer rich text link")
1757                 << nestedText << 240.
1758                 << "textFormat: Text.RichText"
1759                 << (PointVector() << metrics.characterRectangle(22).center())
1760                 << outerLink;
1761         QTest::newRow("click on right outer rich text link")
1762                 << nestedText << 240.
1763                 << "textFormat: Text.RichText"
1764                 << (PointVector() << metrics.characterRectangle(27).center())
1765                 << outerLink;
1766         QTest::newRow("click on inner rich text link left")
1767                 << nestedText << 240.
1768                 << "textFormat: Text.RichText"
1769                 << (PointVector() << metrics.characterRectangle(23).center())
1770                 << innerLink;
1771         QTest::newRow("click on inner rich text link right")
1772                 << nestedText << 240.
1773                 << "textFormat: Text.RichText"
1774                 << (PointVector() << metrics.characterRectangle(26).center())
1775                 << innerLink;
1776         QTest::newRow("drag from inner to outer rich text link")
1777                 << nestedText << 240.
1778                 << "textFormat: Text.RichText"
1779                 << (PointVector()
1780                     << metrics.characterRectangle(25).center()
1781                     << metrics.characterRectangle(30).center())
1782                 << QString();
1783         QTest::newRow("drag from outer to inner rich text link")
1784                 << nestedText << 240.
1785                 << "textFormat: Text.RichText"
1786                 << (PointVector()
1787                     << metrics.characterRectangle(30).center()
1788                     << metrics.characterRectangle(25).center())
1789                 << QString();
1790     }
1791 }
1792
1793 void tst_qquicktext::clickLink()
1794 {
1795     QFETCH(QString, text);
1796     QFETCH(qreal, width);
1797     QFETCH(QString, bindings);
1798     QFETCH(PointVector, mousePositions);
1799     QFETCH(QString, link);
1800
1801     QString componentStr =
1802             "import QtQuick 2.0\nText {\n"
1803                 "width: " + QString::number(width) + "\n"
1804                 "height: 320\n"
1805                 "text: \"" + text + "\"\n"
1806                 "" + bindings + "\n"
1807             "}";
1808     QQmlComponent textComponent(&engine);
1809     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1810     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1811
1812     QVERIFY(textObject != 0);
1813
1814     LinkTest test;
1815     QObject::connect(textObject, SIGNAL(linkActivated(QString)), &test, SLOT(linkClicked(QString)));
1816
1817     QVERIFY(mousePositions.count() > 0);
1818
1819     QPointF mousePosition = mousePositions.first();
1820     {
1821         QMouseEvent me(QEvent::MouseButtonPress, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1822         static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
1823     }
1824
1825     for (int i = 1; i < mousePositions.count(); ++i) {
1826         mousePosition = mousePositions.at(i);
1827
1828         QMouseEvent me(QEvent::MouseMove, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1829         static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
1830     }
1831
1832     {
1833         QMouseEvent me(QEvent::MouseButtonRelease, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1834         static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
1835     }
1836
1837     QCOMPARE(test.link, link);
1838
1839     delete textObject;
1840 }
1841
1842 void tst_qquicktext::baseUrl()
1843 {
1844     QUrl localUrl("file:///tests/text.qml");
1845     QUrl remoteUrl("http://qt.nokia.com/test.qml");
1846
1847     QQmlComponent textComponent(&engine);
1848     textComponent.setData("import QtQuick 2.0\n Text {}", localUrl);
1849     QQuickText *textObject = qobject_cast<QQuickText *>(textComponent.create());
1850
1851     QCOMPARE(textObject->baseUrl(), localUrl);
1852
1853     QSignalSpy spy(textObject, SIGNAL(baseUrlChanged()));
1854
1855     textObject->setBaseUrl(localUrl);
1856     QCOMPARE(textObject->baseUrl(), localUrl);
1857     QCOMPARE(spy.count(), 0);
1858
1859     textObject->setBaseUrl(remoteUrl);
1860     QCOMPARE(textObject->baseUrl(), remoteUrl);
1861     QCOMPARE(spy.count(), 1);
1862
1863     textObject->resetBaseUrl();
1864     QCOMPARE(textObject->baseUrl(), localUrl);
1865     QCOMPARE(spy.count(), 2);
1866 }
1867
1868 void tst_qquicktext::embeddedImages_data()
1869 {
1870     QTest::addColumn<QUrl>("qmlfile");
1871     QTest::addColumn<QString>("error");
1872     QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
1873     QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
1874         << testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML Text: Cannot open: " + testFileUrl("http/notexists.png").toString();
1875     QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
1876     QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
1877     QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
1878                                   << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error downloading " SERVER_ADDR "/notexists.png - server replied: Not found";
1879     QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
1880 }
1881
1882 void tst_qquicktext::embeddedImages()
1883 {
1884     // Tests QTBUG-9900
1885
1886     QFETCH(QUrl, qmlfile);
1887     QFETCH(QString, error);
1888
1889     TestHTTPServer server(SERVER_PORT);
1890     server.serveDirectory(testFile("http"));
1891
1892     if (!error.isEmpty())
1893         QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
1894
1895     QQuickView *view = new QQuickView(qmlfile);
1896     view->show();
1897     view->requestActivateWindow();
1898     QVERIFY(QTest::qWaitForWindowActive(view));
1899     QQuickText *textObject = qobject_cast<QQuickText*>(view->rootObject());
1900
1901     QVERIFY(textObject != 0);
1902     QTRY_COMPARE(textObject->resourcesLoading(), 0);
1903
1904     QPixmap pm(testFile("http/exists.png"));
1905     if (error.isEmpty()) {
1906         QCOMPARE(textObject->width(), double(pm.width()));
1907         QCOMPARE(textObject->height(), double(pm.height()));
1908     } else {
1909         QVERIFY(16 != pm.width()); // check test is effective
1910         QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
1911         QCOMPARE(textObject->height(), 16.0);
1912     }
1913
1914     delete view;
1915 }
1916
1917 void tst_qquicktext::lineCount()
1918 {
1919     QQuickView *window = createView(testFile("lineCount.qml"));
1920
1921     QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
1922     QVERIFY(myText != 0);
1923
1924     QVERIFY(myText->lineCount() > 1);
1925     QVERIFY(!myText->truncated());
1926     QCOMPARE(myText->maximumLineCount(), INT_MAX);
1927
1928     myText->setMaximumLineCount(2);
1929     QCOMPARE(myText->lineCount(), 2);
1930     QCOMPARE(myText->truncated(), true);
1931     QCOMPARE(myText->maximumLineCount(), 2);
1932
1933     myText->resetMaximumLineCount();
1934     QCOMPARE(myText->maximumLineCount(), INT_MAX);
1935     QCOMPARE(myText->truncated(), false);
1936
1937     myText->setElideMode(QQuickText::ElideRight);
1938     myText->setMaximumLineCount(2);
1939     QCOMPARE(myText->lineCount(), 2);
1940     QCOMPARE(myText->truncated(), true);
1941     QCOMPARE(myText->maximumLineCount(), 2);
1942
1943     delete window;
1944 }
1945
1946 void tst_qquicktext::lineHeight()
1947 {
1948     QQuickView *window = createView(testFile("lineHeight.qml"));
1949
1950     QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
1951     QVERIFY(myText != 0);
1952
1953     QVERIFY(myText->lineHeight() == 1);
1954     QVERIFY(myText->lineHeightMode() == QQuickText::ProportionalHeight);
1955
1956     qreal h = myText->height();
1957     myText->setLineHeight(1.5);
1958     QCOMPARE(myText->height(), qreal(qCeil(h)) * 1.5);
1959
1960     myText->setLineHeightMode(QQuickText::FixedHeight);
1961     myText->setLineHeight(20);
1962     QCOMPARE(myText->height(), myText->lineCount() * 20.0);
1963
1964     myText->setText("Lorem ipsum sit <b>amet</b>, consectetur adipiscing elit. Integer felis nisl, varius in pretium nec, venenatis non erat. Proin lobortis interdum dictum.");
1965     myText->setLineHeightMode(QQuickText::ProportionalHeight);
1966     myText->setLineHeight(1.0);
1967
1968     qreal h2 = myText->height();
1969     myText->setLineHeight(2.0);
1970     QVERIFY(myText->height() == h2 * 2.0);
1971
1972     myText->setLineHeightMode(QQuickText::FixedHeight);
1973     myText->setLineHeight(10);
1974     QCOMPARE(myText->height(), myText->lineCount() * 10.0);
1975
1976     delete window;
1977 }
1978
1979 void tst_qquicktext::implicitSize_data()
1980 {
1981     QTest::addColumn<QString>("text");
1982     QTest::addColumn<QString>("width");
1983     QTest::addColumn<QString>("wrap");
1984     QTest::addColumn<QString>("elide");
1985     QTest::addColumn<QString>("format");
1986     QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.NoWrap" << "Text.ElideNone" << "Text.PlainText";
1987     QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 50" << "Text.NoWrap" << "Text.ElideNone" << "Text.RichText";
1988     QTest::newRow("styledtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 50" << "Text.NoWrap" << "Text.ElideNone" << "Text.StyledText";
1989     QTest::newRow("plain, 0 width") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.NoWrap" << "Text.ElideNone" << "Text.PlainText";
1990     QTest::newRow("plain, elide") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.NoWrap" << "Text.ElideRight" << "Text.PlainText";
1991     QTest::newRow("plain, 0 width, elide") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.NoWrap" << "Text.ElideRight" << "Text.PlainText";
1992     QTest::newRow("richtext, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 0" << "Text.NoWrap" << "Text.ElideNone" << "Text.RichText";
1993     QTest::newRow("styledtext, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 0" << "Text.NoWrap" << "Text.ElideNone" << "Text.StyledText";
1994     QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.Wrap" << "Text.ElideNone" << "Text.PlainText";
1995     QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "50" << "Text.Wrap" << "Text.ElideNone" << "Text.RichText";
1996     QTest::newRow("styledtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "50" << "Text.Wrap" << "Text.ElideNone" << "Text.StyledText";
1997     QTest::newRow("plain_wrap, 0 width") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.Wrap" << "Text.ElideNone" << "Text.PlainText";
1998     QTest::newRow("plain_wrap, elide") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.Wrap" << "Text.ElideRight" << "Text.PlainText";
1999     QTest::newRow("plain_wrap, 0 width, elide") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.Wrap" << "Text.ElideRight" << "Text.PlainText";
2000     QTest::newRow("richtext_wrap, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "0" << "Text.Wrap" << "Text.ElideNone" << "Text.RichText";
2001     QTest::newRow("styledtext_wrap, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "0" << "Text.Wrap" << "Text.ElideNone" << "Text.StyledText";
2002 }
2003
2004 void tst_qquicktext::implicitSize()
2005 {
2006     QFETCH(QString, text);
2007     QFETCH(QString, width);
2008     QFETCH(QString, format);
2009     QFETCH(QString, wrap);
2010     QFETCH(QString, elide);
2011     QString componentStr = "import QtQuick 2.0\nText { "
2012             "property real iWidth: implicitWidth; "
2013             "text: \"" + text + "\"; "
2014             "width: " + width + "; "
2015             "textFormat: " + format + "; "
2016             "wrapMode: " + wrap + "; "
2017             "elide: " + elide + "; "
2018             "maximumLineCount: 2 }";
2019     QQmlComponent textComponent(&engine);
2020     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2021     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
2022
2023     QVERIFY(textObject->width() < textObject->implicitWidth());
2024     QVERIFY(textObject->height() == textObject->implicitHeight());
2025     QCOMPARE(textObject->property("iWidth").toReal(), textObject->implicitWidth());
2026
2027     textObject->resetWidth();
2028     QVERIFY(textObject->width() == textObject->implicitWidth());
2029     QVERIFY(textObject->height() == textObject->implicitHeight());
2030
2031     delete textObject;
2032 }
2033
2034 void tst_qquicktext::contentSize()
2035 {
2036     QString componentStr = "import QtQuick 2.0\nText { width: 75; height: 16; font.pixelSize: 10 }";
2037     QQmlComponent textComponent(&engine);
2038     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2039     QScopedPointer<QObject> object(textComponent.create());
2040     QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
2041
2042     QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
2043
2044     textObject->setText("The quick red fox jumped over the lazy brown dog");
2045
2046     QVERIFY(textObject->contentWidth() > textObject->width());
2047     QVERIFY(textObject->contentHeight() < textObject->height());
2048     QCOMPARE(spy.count(), 1);
2049
2050     textObject->setWrapMode(QQuickText::WordWrap);
2051     QVERIFY(textObject->contentWidth() <= textObject->width());
2052     QVERIFY(textObject->contentHeight() > textObject->height());
2053     QCOMPARE(spy.count(), 2);
2054
2055     textObject->setElideMode(QQuickText::ElideRight);
2056     QVERIFY(textObject->contentWidth() <= textObject->width());
2057     QVERIFY(textObject->contentHeight() < textObject->height());
2058     QCOMPARE(spy.count(), 3);
2059     int spyCount = 3;
2060     qreal elidedWidth = textObject->contentWidth();
2061
2062     textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
2063     QVERIFY(textObject->contentWidth() <= textObject->width());
2064     QVERIFY(textObject->contentHeight() < textObject->height());
2065     // this text probably won't have the same elided width, but it's not guaranteed.
2066     if (textObject->contentWidth() != elidedWidth)
2067         QCOMPARE(spy.count(), ++spyCount);
2068     else
2069         QCOMPARE(spy.count(), spyCount);
2070
2071     textObject->setElideMode(QQuickText::ElideNone);
2072     QVERIFY(textObject->contentWidth() > textObject->width());
2073     QVERIFY(textObject->contentHeight() > textObject->height());
2074     QCOMPARE(spy.count(), ++spyCount);
2075 }
2076
2077 void tst_qquicktext::geometryChanged()
2078 {
2079     // Test that text is re-laid out when the geometry of the item by verifying changes in content
2080     // size.  Implicit width is also tested as that in combination with item geometry provides a
2081     // reference for expected content sizes.
2082
2083     QString componentStr = "import QtQuick 2.0\nText { font.family: \"__Qt__Box__Engine__\"; font.pixelSize: 10 }";
2084     QQmlComponent textComponent(&engine);
2085     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2086     QScopedPointer<QObject> object(textComponent.create());
2087     QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
2088
2089     const qreal implicitHeight = textObject->implicitHeight();
2090
2091     const qreal widths[] = { 100, 2000, 3000, -100, 100 };
2092     const qreal heights[] = { implicitHeight, 2000, 3000, -implicitHeight, implicitHeight };
2093
2094     QCOMPARE(textObject->implicitWidth(), 0.);
2095     QVERIFY(implicitHeight > 0.);
2096     QCOMPARE(textObject->width(), textObject->implicitWidth());
2097     QCOMPARE(textObject->height(), implicitHeight);
2098     QCOMPARE(textObject->contentWidth(), textObject->implicitWidth());
2099     QCOMPARE(textObject->contentHeight(), implicitHeight);
2100
2101     textObject->setText("The quick red fox jumped over the lazy brown dog");
2102
2103     const qreal implicitWidth = textObject->implicitWidth();
2104
2105     QVERIFY(implicitWidth > 0.);
2106     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2107     QCOMPARE(textObject->width(), textObject->implicitWidth());
2108     QCOMPARE(textObject->height(), textObject->implicitHeight());
2109     QCOMPARE(textObject->contentWidth(), textObject->implicitWidth());
2110     QCOMPARE(textObject->contentHeight(), textObject->implicitHeight());
2111
2112     // Changing the geometry with no eliding, or wrapping doesn't change the content size.
2113     for (int i = 0; i < 5; ++i) {
2114         textObject->setWidth(widths[i]);
2115         QCOMPARE(textObject->implicitWidth(), implicitWidth);
2116         QCOMPARE(textObject->implicitHeight(), implicitHeight);
2117         QCOMPARE(textObject->width(), widths[i]);
2118         QCOMPARE(textObject->height(), implicitHeight);
2119         QCOMPARE(textObject->contentWidth(), implicitWidth);
2120         QCOMPARE(textObject->contentHeight(), implicitHeight);
2121     }
2122
2123     // With eliding enabled the content width is bounded to the item width, but is never
2124     // larger than the implicit width.
2125     textObject->setElideMode(QQuickText::ElideRight);
2126     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2127     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2128     QCOMPARE(textObject->width(), 100.);
2129     QCOMPARE(textObject->height(), implicitHeight);
2130     QVERIFY(textObject->contentWidth() <= 100.);
2131     QCOMPARE(textObject->contentHeight(), implicitHeight);
2132
2133     textObject->setWidth(2000.);
2134     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2135     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2136     QCOMPARE(textObject->width(), 2000.);
2137     QCOMPARE(textObject->height(), implicitHeight);
2138     QCOMPARE(textObject->contentWidth(), implicitWidth);
2139     QCOMPARE(textObject->contentHeight(), implicitHeight);
2140
2141     textObject->setWidth(3000.);
2142     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2143     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2144     QCOMPARE(textObject->width(), 3000.);
2145     QCOMPARE(textObject->height(), implicitHeight);
2146     QCOMPARE(textObject->contentWidth(), implicitWidth);
2147     QCOMPARE(textObject->contentHeight(), implicitHeight);
2148
2149     textObject->setWidth(-100);
2150     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2151     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2152     QCOMPARE(textObject->width(), -100.);
2153     QCOMPARE(textObject->height(), implicitHeight);
2154     QCOMPARE(textObject->contentWidth(), 0.);
2155     QCOMPARE(textObject->contentHeight(), implicitHeight);
2156
2157     textObject->setWidth(100.);
2158     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2159     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2160     QCOMPARE(textObject->width(), 100.);
2161     QCOMPARE(textObject->height(), implicitHeight);
2162     QVERIFY(textObject->contentWidth() <= 100.);
2163     QCOMPARE(textObject->contentHeight(), implicitHeight);
2164
2165     // With wrapping enabled the implicit height changes with the width.
2166     textObject->setElideMode(QQuickText::ElideNone);
2167     textObject->setWrapMode(QQuickText::Wrap);
2168     const qreal wrappedImplicitHeight = textObject->implicitHeight();
2169
2170     QVERIFY(wrappedImplicitHeight > implicitHeight);
2171
2172     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2173     QCOMPARE(textObject->width(), 100.);
2174     QCOMPARE(textObject->height(), wrappedImplicitHeight);
2175     QVERIFY(textObject->contentWidth() <= 100.);
2176     QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
2177
2178     textObject->setWidth(2000.);
2179     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2180     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2181     QCOMPARE(textObject->width(), 2000.);
2182     QCOMPARE(textObject->height(), implicitHeight);
2183     QCOMPARE(textObject->contentWidth(), implicitWidth);
2184     QCOMPARE(textObject->contentHeight(), implicitHeight);
2185
2186     textObject->setWidth(3000.);
2187     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2188     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2189     QCOMPARE(textObject->width(), 3000.);
2190     QCOMPARE(textObject->height(), implicitHeight);
2191     QCOMPARE(textObject->contentWidth(), implicitWidth);
2192     QCOMPARE(textObject->contentHeight(), implicitHeight);
2193
2194     textObject->setWidth(-100);
2195     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2196     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2197     QCOMPARE(textObject->width(), -100.);
2198     QCOMPARE(textObject->height(), implicitHeight);
2199     QCOMPARE(textObject->contentWidth(), implicitWidth);    // 0 or negative width item won't wrap.
2200     QCOMPARE(textObject->contentHeight(), implicitHeight);
2201
2202     textObject->setWidth(100.);
2203     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2204     QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
2205     QCOMPARE(textObject->width(), 100.);
2206     QCOMPARE(textObject->height(), wrappedImplicitHeight);
2207     QVERIFY(textObject->contentWidth() <= 100.);
2208     QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
2209
2210     // With no eliding or maximum line count the content height is the same as the implicit height.
2211     for (int i = 0; i < 5; ++i) {
2212         textObject->setHeight(heights[i]);
2213         QCOMPARE(textObject->implicitWidth(), implicitWidth);
2214         QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
2215         QCOMPARE(textObject->width(), 100.);
2216         QCOMPARE(textObject->height(), heights[i]);
2217         QVERIFY(textObject->contentWidth() <= 100.);
2218         QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
2219     }
2220
2221     // The implicit height is unaffected by eliding but the content height will change.
2222     textObject->setElideMode(QQuickText::ElideRight);
2223
2224     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2225     QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
2226     QCOMPARE(textObject->width(), 100.);
2227     QCOMPARE(textObject->height(), implicitHeight);
2228     QVERIFY(textObject->contentWidth() <= 100.);
2229     QCOMPARE(textObject->contentHeight(), implicitHeight);
2230
2231     textObject->setHeight(2000);
2232     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2233     QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
2234     QCOMPARE(textObject->width(), 100.);
2235     QCOMPARE(textObject->height(), 2000.);
2236     QVERIFY(textObject->contentWidth() <= 100.);
2237     QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
2238
2239     textObject->setHeight(3000);
2240     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2241     QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
2242     QCOMPARE(textObject->width(), 100.);
2243     QCOMPARE(textObject->height(), 3000.);
2244     QVERIFY(textObject->contentWidth() <= 100.);
2245     QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
2246
2247     textObject->setHeight(-implicitHeight);
2248     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2249     QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
2250     QCOMPARE(textObject->width(), 100.);
2251     QCOMPARE(textObject->height(), -implicitHeight);
2252     QVERIFY(textObject->contentWidth() <= 0.);
2253     QCOMPARE(textObject->contentHeight(), implicitHeight);  // content height is never less than font height. seems a little odd in this instance.
2254
2255     textObject->setHeight(implicitHeight);
2256     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2257     QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
2258     QCOMPARE(textObject->width(), 100.);
2259     QCOMPARE(textObject->height(), implicitHeight);
2260     QVERIFY(textObject->contentWidth() <= 100.);
2261     QCOMPARE(textObject->contentHeight(), implicitHeight);
2262
2263     // Varying the height with a maximum line count but no eliding won't affect the content height.
2264     textObject->setElideMode(QQuickText::ElideNone);
2265     textObject->setMaximumLineCount(2);
2266     textObject->resetHeight();
2267
2268     const qreal maxLineCountImplicitHeight = textObject->implicitHeight();
2269     QVERIFY(maxLineCountImplicitHeight > implicitHeight);
2270     QVERIFY(maxLineCountImplicitHeight < wrappedImplicitHeight);
2271
2272     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2273     QCOMPARE(textObject->width(), 100.);
2274     QCOMPARE(textObject->height(), maxLineCountImplicitHeight);
2275     QVERIFY(textObject->contentWidth() <= 100.);
2276     QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
2277
2278     for (int i = 0; i < 5; ++i) {
2279         textObject->setHeight(heights[i]);
2280         QCOMPARE(textObject->implicitWidth(), implicitWidth);
2281         QCOMPARE(textObject->implicitHeight(), maxLineCountImplicitHeight);
2282         QCOMPARE(textObject->width(), 100.);
2283         QCOMPARE(textObject->height(), heights[i]);
2284         QVERIFY(textObject->contentWidth() <= 100.);
2285         QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
2286     }
2287
2288     // Varying the width with a maximum line count won't increase the implicit height beyond the
2289     // height of the maximum number of lines.
2290     textObject->setWidth(2000.);
2291     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2292     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2293     QCOMPARE(textObject->width(), 2000.);
2294     QCOMPARE(textObject->height(), implicitHeight);
2295     QCOMPARE(textObject->contentWidth(), implicitWidth);
2296     QCOMPARE(textObject->contentHeight(), implicitHeight);
2297
2298     textObject->setWidth(3000.);
2299     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2300     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2301     QCOMPARE(textObject->width(), 3000.);
2302     QCOMPARE(textObject->height(), implicitHeight);
2303     QCOMPARE(textObject->contentWidth(), implicitWidth);
2304     QCOMPARE(textObject->contentHeight(), implicitHeight);
2305
2306     textObject->setWidth(-100);
2307     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2308     QCOMPARE(textObject->implicitHeight(), implicitHeight);
2309     QCOMPARE(textObject->width(), -100.);
2310     QCOMPARE(textObject->height(), implicitHeight);
2311     QCOMPARE(textObject->contentWidth(), implicitWidth);    // 0 or negative width item won't wrap.
2312     QCOMPARE(textObject->contentHeight(), implicitHeight);
2313
2314     textObject->setWidth(50.);
2315     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2316     QCOMPARE(textObject->implicitHeight(), maxLineCountImplicitHeight);
2317     QCOMPARE(textObject->width(), 50.);
2318     QCOMPARE(textObject->height(), implicitHeight);
2319     QVERIFY(textObject->contentWidth() <= 50.);
2320     QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
2321
2322     textObject->setWidth(100.);
2323     QCOMPARE(textObject->implicitWidth(), implicitWidth);
2324     QCOMPARE(textObject->implicitHeight(), maxLineCountImplicitHeight);
2325     QCOMPARE(textObject->width(), 100.);
2326     QCOMPARE(textObject->height(), implicitHeight);
2327     QVERIFY(textObject->contentWidth() <= 100.);
2328     QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
2329 }
2330
2331 void tst_qquicktext::implicitSizeBinding_data()
2332 {
2333     implicitSize_data();
2334 }
2335
2336 void tst_qquicktext::implicitSizeBinding()
2337 {
2338     QFETCH(QString, text);
2339     QFETCH(QString, wrap);
2340     QFETCH(QString, format);
2341     QString componentStr = "import QtQuick 2.0\nText { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + "; textFormat: " + format + " }";
2342
2343     QQmlComponent textComponent(&engine);
2344     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2345     QScopedPointer<QObject> object(textComponent.create());
2346     QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
2347
2348     QCOMPARE(textObject->width(), textObject->implicitWidth());
2349     QCOMPARE(textObject->height(), textObject->implicitHeight());
2350
2351     textObject->resetWidth();
2352     QCOMPARE(textObject->width(), textObject->implicitWidth());
2353     QCOMPARE(textObject->height(), textObject->implicitHeight());
2354
2355     textObject->resetHeight();
2356     QCOMPARE(textObject->width(), textObject->implicitWidth());
2357     QCOMPARE(textObject->height(), textObject->implicitHeight());
2358 }
2359
2360 void tst_qquicktext::boundingRect_data()
2361 {
2362     QTest::addColumn<QString>("format");
2363     QTest::newRow("PlainText") << "Text.PlainText";
2364     QTest::newRow("StyledText") << "Text.StyledText";
2365     QTest::newRow("RichText") << "Text.RichText";
2366 }
2367
2368 void tst_qquicktext::boundingRect()
2369 {
2370     QFETCH(QString, format);
2371
2372     QQmlComponent component(&engine);
2373     component.setData("import QtQuick 2.0\n Text { textFormat:" + format.toUtf8() + "}", QUrl());
2374     QScopedPointer<QObject> object(component.create());
2375     QQuickText *text = qobject_cast<QQuickText *>(object.data());
2376     QVERIFY(text);
2377
2378     QCOMPARE(text->boundingRect().x(), qreal(0));
2379     QCOMPARE(text->boundingRect().y(), qreal(0));
2380     QCOMPARE(text->boundingRect().width(), qreal(0));
2381     QCOMPARE(text->boundingRect().height(), qreal(qCeil(QFontMetricsF(text->font()).height())));
2382
2383     text->setText("Hello World");
2384
2385     QTextLayout layout(text->text());
2386     layout.setFont(text->font());
2387
2388     if (!qmlDisableDistanceField()) {
2389         QTextOption option;
2390         option.setUseDesignMetrics(true);
2391         layout.setTextOption(option);
2392     }
2393     layout.beginLayout();
2394     QTextLine line = layout.createLine();
2395     layout.endLayout();
2396
2397     QCOMPARE(text->boundingRect().x(), qreal(0));
2398     QCOMPARE(text->boundingRect().y(), qreal(0));
2399     QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
2400     QCOMPARE(text->boundingRect().height(), line.height());
2401
2402     // the size of the bounding rect shouldn't be bounded by the size of item.
2403     text->setWidth(text->width() / 2);
2404     QCOMPARE(text->boundingRect().x(), qreal(0));
2405     QCOMPARE(text->boundingRect().y(), qreal(0));
2406     QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
2407     QCOMPARE(text->boundingRect().height(), line.height());
2408
2409     text->setHeight(text->height() * 2);
2410     QCOMPARE(text->boundingRect().x(), qreal(0));
2411     QCOMPARE(text->boundingRect().y(), qreal(0));
2412     QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
2413     QCOMPARE(text->boundingRect().height(), line.height());
2414
2415     text->setHAlign(QQuickText::AlignRight);
2416     QCOMPARE(text->boundingRect().x(), text->width() - line.naturalTextWidth());
2417     QCOMPARE(text->boundingRect().y(), qreal(0));
2418     QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
2419     QCOMPARE(text->boundingRect().height(), line.height());
2420
2421     text->setWrapMode(QQuickText::Wrap);
2422     QCOMPARE(text->boundingRect().right(), text->width());
2423     QCOMPARE(text->boundingRect().y(), qreal(0));
2424     QVERIFY(text->boundingRect().width() < line.naturalTextWidth());
2425     QVERIFY(text->boundingRect().height() > line.height());
2426
2427     text->setVAlign(QQuickText::AlignBottom);
2428     QCOMPARE(text->boundingRect().right(), text->width());
2429     QCOMPARE(text->boundingRect().bottom(), text->height());
2430     QVERIFY(text->boundingRect().width() < line.naturalTextWidth());
2431     QVERIFY(text->boundingRect().height() > line.height());
2432 }
2433
2434 void tst_qquicktext::clipRect()
2435 {
2436     QQmlComponent component(&engine);
2437     component.setData("import QtQuick 2.0\n Text {}", QUrl());
2438     QScopedPointer<QObject> object(component.create());
2439     QQuickText *text = qobject_cast<QQuickText *>(object.data());
2440     QVERIFY(text);
2441
2442     QTextLayout layout;
2443     layout.setFont(text->font());
2444
2445     QCOMPARE(text->clipRect().x(), qreal(0));
2446     QCOMPARE(text->clipRect().y(), qreal(0));
2447     QCOMPARE(text->clipRect().width(), text->width());
2448     QCOMPARE(text->clipRect().height(), text->height());
2449
2450     text->setText("Hello World");
2451
2452     QCOMPARE(text->clipRect().x(), qreal(0));
2453     QCOMPARE(text->clipRect().y(), qreal(0));
2454     QCOMPARE(text->clipRect().width(), text->width());
2455     QCOMPARE(text->clipRect().height(), text->height());
2456
2457     // Clip rect follows the item not content dimensions.
2458     text->setWidth(text->width() / 2);
2459     QCOMPARE(text->clipRect().x(), qreal(0));
2460     QCOMPARE(text->clipRect().y(), qreal(0));
2461     QCOMPARE(text->clipRect().width(), text->width());
2462     QCOMPARE(text->clipRect().height(), text->height());
2463
2464     text->setHeight(text->height() * 2);
2465     QCOMPARE(text->clipRect().x(), qreal(0));
2466     QCOMPARE(text->clipRect().y(), qreal(0));
2467     QCOMPARE(text->clipRect().width(), text->width());
2468     QCOMPARE(text->clipRect().height(), text->height());
2469
2470     // Setting a style adds a small amount of padding to the clip rect.
2471     text->setStyle(QQuickText::Outline);
2472     QCOMPARE(text->clipRect().x(), qreal(-1));
2473     QCOMPARE(text->clipRect().y(), qreal(0));
2474     QCOMPARE(text->clipRect().width(), text->width() + 2);
2475     QCOMPARE(text->clipRect().height(), text->height() + 2);
2476 }
2477
2478 void tst_qquicktext::lineLaidOut()
2479 {
2480     QQuickView *window = createView(testFile("lineLayout.qml"));
2481
2482     QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
2483     QVERIFY(myText != 0);
2484
2485     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
2486     QVERIFY(textPrivate != 0);
2487
2488     QVERIFY(!textPrivate->extra.isAllocated());
2489
2490     for (int i = 0; i < textPrivate->layout.lineCount(); ++i) {
2491         QRectF r = textPrivate->layout.lineAt(i).rect();
2492         QVERIFY(r.width() == i * 15);
2493         if (i >= 30)
2494             QVERIFY(r.x() == r.width() + 30);
2495         if (i >= 60) {
2496             QVERIFY(r.x() == r.width() * 2 + 60);
2497             QVERIFY(r.height() == 20);
2498         }
2499     }
2500
2501     delete window;
2502 }
2503
2504 void tst_qquicktext::lineLaidOutRelayout()
2505 {
2506     QQuickView *window = createView(testFile("lineLayoutRelayout.qml"));
2507
2508     window->show();
2509     window->requestActivateWindow();
2510     QVERIFY(QTest::qWaitForWindowActive(window));
2511
2512     QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
2513     QVERIFY(myText != 0);
2514
2515     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
2516     QVERIFY(textPrivate != 0);
2517
2518     QVERIFY(!textPrivate->extra.isAllocated());
2519
2520     qreal maxH = 0;
2521     for (int i = 0; i < textPrivate->layout.lineCount(); ++i) {
2522         QRectF r = textPrivate->layout.lineAt(i).rect();
2523
2524         if (r.x() == 0) {
2525             QCOMPARE(r.y(), i * r.height());
2526             maxH = qMax(maxH, r.y() + r.height());
2527         } else {
2528             QCOMPARE(r.x(), myText->width() / 2);
2529             QCOMPARE(r.y(), (i * r.height()) - maxH);
2530         }
2531     }
2532
2533     delete window;
2534 }
2535
2536 void tst_qquicktext::imgTagsBaseUrl_data()
2537 {
2538     QTest::addColumn<QUrl>("src");
2539     QTest::addColumn<QUrl>("baseUrl");
2540     QTest::addColumn<QUrl>("contextUrl");
2541     QTest::addColumn<qreal>("imgHeight");
2542
2543     QTest::newRow("absolute local")
2544             << testFileUrl("images/heart200.png")
2545             << QUrl()
2546             << QUrl()
2547             << 181.;
2548     QTest::newRow("relative local context 1")
2549             << QUrl("images/heart200.png")
2550             << QUrl()
2551             << testFileUrl("/app.qml")
2552             << 181.;
2553     QTest::newRow("relative local context 2")
2554             << QUrl("heart200.png")
2555             << QUrl()
2556             << testFileUrl("images/app.qml")
2557             << 181.;
2558     QTest::newRow("relative local base 1")
2559             << QUrl("images/heart200.png")
2560             << testFileUrl("")
2561             << testFileUrl("nonexistant/app.qml")
2562             << 181.;
2563     QTest::newRow("relative local base 2")
2564             << QUrl("heart200.png")
2565             << testFileUrl("images/")
2566             << testFileUrl("nonexistant/app.qml")
2567             << 181.;
2568     QTest::newRow("base relative to local context")
2569             << QUrl("heart200.png")
2570             << testFileUrl("images/")
2571             << testFileUrl("/app.qml")
2572             << 181.;
2573
2574     QTest::newRow("absolute remote")
2575             << QUrl(SERVER_ADDR "/images/heart200.png")
2576             << QUrl()
2577             << QUrl()
2578             << 181.;
2579     QTest::newRow("relative remote base 1")
2580             << QUrl("images/heart200.png")
2581             << QUrl(SERVER_ADDR "/")
2582             << testFileUrl("nonexistant/app.qml")
2583             << 181.;
2584     QTest::newRow("relative remote base 2")
2585             << QUrl("heart200.png")
2586             << QUrl(SERVER_ADDR "/images/")
2587             << testFileUrl("nonexistant/app.qml")
2588             << 181.;
2589 }
2590
2591 void tst_qquicktext::imgTagsBaseUrl()
2592 {
2593     QFETCH(QUrl, src);
2594     QFETCH(QUrl, baseUrl);
2595     QFETCH(QUrl, contextUrl);
2596     QFETCH(qreal, imgHeight);
2597
2598     TestHTTPServer server(SERVER_PORT);
2599     server.serveDirectory(testFile(""));
2600
2601     QByteArray baseUrlFragment;
2602     if (!baseUrl.isEmpty())
2603         baseUrlFragment = "; baseUrl: \"" + baseUrl.toEncoded() + "\"";
2604     QByteArray componentStr = "import QtQuick 2.0\nText { text: \"This is a test <img src=\\\"" + src.toEncoded() + "\\\">\"" + baseUrlFragment + " }";
2605
2606     QQmlComponent component(&engine);
2607     component.setData(componentStr, contextUrl);
2608     QScopedPointer<QObject> object(component.create());
2609     QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
2610     QVERIFY(textObject);
2611
2612     QCoreApplication::processEvents();
2613
2614     QTRY_COMPARE(textObject->height(), imgHeight);
2615 }
2616
2617 void tst_qquicktext::imgTagsAlign_data()
2618 {
2619     QTest::addColumn<QString>("src");
2620     QTest::addColumn<int>("imgHeight");
2621     QTest::addColumn<QString>("align");
2622     QTest::newRow("heart-bottom") << "data/images/heart200.png" << 181 <<  "bottom";
2623     QTest::newRow("heart-middle") << "data/images/heart200.png" << 181 <<  "middle";
2624     QTest::newRow("heart-top") << "data/images/heart200.png" << 181 <<  "top";
2625     QTest::newRow("starfish-bottom") << "data/images/starfish_2.png" << 217 <<  "bottom";
2626     QTest::newRow("starfish-middle") << "data/images/starfish_2.png" << 217 <<  "middle";
2627     QTest::newRow("starfish-top") << "data/images/starfish_2.png" << 217 <<  "top";
2628 }
2629
2630 void tst_qquicktext::imgTagsAlign()
2631 {
2632     QFETCH(QString, src);
2633     QFETCH(int, imgHeight);
2634     QFETCH(QString, align);
2635     QString componentStr = "import QtQuick 2.0\nText { text: \"This is a test <img src=\\\"" + src + "\\\" align=\\\"" + align + "\\\"> of image.\" }";
2636     QQmlComponent textComponent(&engine);
2637     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2638     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
2639
2640     QVERIFY(textObject != 0);
2641     QVERIFY(textObject->height() == imgHeight);
2642
2643     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
2644     QVERIFY(textPrivate != 0);
2645
2646     QRectF br = textPrivate->layout.boundingRect();
2647     if (align == "bottom")
2648         QVERIFY(br.y() == imgHeight - br.height());
2649     else if (align == "middle")
2650         QVERIFY(br.y() == imgHeight / 2.0 - br.height() / 2.0);
2651     else if (align == "top")
2652         QVERIFY(br.y() == 0);
2653
2654     delete textObject;
2655 }
2656
2657 void tst_qquicktext::imgTagsMultipleImages()
2658 {
2659     QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"data/images/starfish_2.png\\\" width=\\\"60\\\" height=\\\"60\\\" > and another one<img src=\\\"data/images/heart200.png\\\" width=\\\"85\\\" height=\\\"85\\\">.\" }";
2660
2661     QQmlComponent textComponent(&engine);
2662     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2663     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
2664
2665     QVERIFY(textObject != 0);
2666     QVERIFY(textObject->height() == 85);
2667
2668     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
2669     QVERIFY(textPrivate != 0);
2670     QVERIFY(textPrivate->visibleImgTags.count() == 2);
2671
2672     delete textObject;
2673 }
2674
2675 void tst_qquicktext::imgTagsElide()
2676 {
2677     QQuickView *window = createView(testFile("imgTagsElide.qml"));
2678     QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
2679     QVERIFY(myText != 0);
2680
2681     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
2682     QVERIFY(textPrivate != 0);
2683     QVERIFY(textPrivate->visibleImgTags.count() == 0);
2684     myText->setMaximumLineCount(20);
2685     QTRY_VERIFY(textPrivate->visibleImgTags.count() == 1);
2686
2687     delete myText;
2688     delete window;
2689 }
2690
2691 void tst_qquicktext::imgTagsUpdates()
2692 {
2693     QQuickView *window = createView(testFile("imgTagsUpdates.qml"));
2694     QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
2695     QVERIFY(myText != 0);
2696
2697     QSignalSpy spy(myText, SIGNAL(contentSizeChanged()));
2698
2699     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
2700     QVERIFY(textPrivate != 0);
2701
2702     myText->setText("This is a heart<img src=\"images/heart200.png\">.");
2703     QVERIFY(textPrivate->visibleImgTags.count() == 1);
2704     QVERIFY(spy.count() == 1);
2705
2706     myText->setMaximumLineCount(2);
2707     myText->setText("This is another heart<img src=\"images/heart200.png\">.");
2708     QTRY_VERIFY(textPrivate->visibleImgTags.count() == 1);
2709
2710     // if maximumLineCount is set and the img tag doesn't have an explicit size
2711     // we relayout twice.
2712     QVERIFY(spy.count() == 3);
2713
2714     delete myText;
2715     delete window;
2716 }
2717
2718 void tst_qquicktext::imgTagsError()
2719 {
2720     QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"data/images/starfish_2.pn\\\" width=\\\"60\\\" height=\\\"60\\\">.\" }";
2721
2722     QQmlComponent textComponent(&engine);
2723     QTest::ignoreMessage(QtWarningMsg, "file::2:1: QML Text: Cannot open: file:data/images/starfish_2.pn");
2724     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2725     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
2726
2727     QVERIFY(textObject != 0);
2728     delete textObject;
2729 }
2730
2731 void tst_qquicktext::fontSizeMode_data()
2732 {
2733     QTest::addColumn<QString>("text");
2734     QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog";
2735     QTest::newRow("styled") << "<b>The quick red fox jumped over the lazy brown dog</b>";
2736 }
2737
2738 void tst_qquicktext::fontSizeMode()
2739 {
2740     QFETCH(QString, text);
2741
2742     QScopedPointer<QQuickView> window(createView(testFile("fontSizeMode.qml")));
2743     window->show();
2744
2745     QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
2746     QVERIFY(myText != 0);
2747
2748     myText->setText(text);
2749     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2750
2751     qreal originalWidth = myText->contentWidth();
2752     qreal originalHeight = myText->contentHeight();
2753
2754     // The original text unwrapped should exceed the width of the item.
2755     QVERIFY(originalWidth > myText->width());
2756     QVERIFY(originalHeight < myText->height());
2757
2758     QFont font = myText->font();
2759     font.setPixelSize(64);
2760
2761     myText->setFont(font);
2762     myText->setFontSizeMode(QQuickText::HorizontalFit);
2763     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2764     // Font size reduced to fit within the width of the item.
2765     qreal horizontalFitWidth = myText->contentWidth();
2766     qreal horizontalFitHeight = myText->contentHeight();
2767     QVERIFY(horizontalFitWidth <= myText->width() + 2); // rounding
2768     QVERIFY(horizontalFitHeight <= myText->height() + 2);
2769
2770     // Elide won't affect the size with HorizontalFit.
2771     myText->setElideMode(QQuickText::ElideRight);
2772     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2773     QVERIFY(!myText->truncated());
2774     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2775     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2776
2777     myText->setElideMode(QQuickText::ElideLeft);
2778     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2779     QVERIFY(!myText->truncated());
2780     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2781     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2782
2783     myText->setElideMode(QQuickText::ElideMiddle);
2784     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2785     QVERIFY(!myText->truncated());
2786     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2787     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2788
2789     myText->setElideMode(QQuickText::ElideNone);
2790     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2791
2792     myText->setFontSizeMode(QQuickText::VerticalFit);
2793     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2794     // Font size increased to fill the height of the item.
2795     qreal verticalFitHeight = myText->contentHeight();
2796     QVERIFY(myText->contentWidth() > myText->width());
2797     QVERIFY(verticalFitHeight <= myText->height() + 2);
2798     QVERIFY(verticalFitHeight > originalHeight);
2799
2800     // Elide won't affect the height of a single line with VerticalFit but will crop the width.
2801     myText->setElideMode(QQuickText::ElideRight);
2802     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2803     QVERIFY(myText->truncated());
2804     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2805     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2806
2807     myText->setElideMode(QQuickText::ElideLeft);
2808     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2809     QVERIFY(myText->truncated());
2810     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2811     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2812
2813     myText->setElideMode(QQuickText::ElideMiddle);
2814     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2815     QVERIFY(myText->truncated());
2816     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2817     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2818
2819     myText->setElideMode(QQuickText::ElideNone);
2820     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2821
2822     myText->setFontSizeMode(QQuickText::Fit);
2823     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2824     // Should be the same as HorizontalFit with no wrapping.
2825     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2826     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2827
2828     // Elide won't affect the size with Fit.
2829     myText->setElideMode(QQuickText::ElideRight);
2830     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2831     QVERIFY(!myText->truncated());
2832     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2833     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2834
2835     myText->setElideMode(QQuickText::ElideLeft);
2836     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2837     QVERIFY(!myText->truncated());
2838     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2839     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2840
2841     myText->setElideMode(QQuickText::ElideMiddle);
2842     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2843     QVERIFY(!myText->truncated());
2844     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2845     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2846
2847     myText->setElideMode(QQuickText::ElideNone);
2848     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2849
2850     myText->setFontSizeMode(QQuickText::FixedSize);
2851     myText->setWrapMode(QQuickText::Wrap);
2852     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2853
2854     originalWidth = myText->contentWidth();
2855     originalHeight = myText->contentHeight();
2856
2857     // The original text wrapped should exceed the height of the item.
2858     QVERIFY(originalWidth <= myText->width() + 2);
2859     QVERIFY(originalHeight > myText->height());
2860
2861     myText->setFontSizeMode(QQuickText::HorizontalFit);
2862     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2863     // HorizontalFit should reduce the font size to minimize wrapping, which brings it back to the
2864     // same size as without text wrapping.
2865     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2866     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2867
2868     // Elide won't affect the size with HorizontalFit.
2869     myText->setElideMode(QQuickText::ElideRight);
2870     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2871     QVERIFY(!myText->truncated());
2872     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2873     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2874
2875     myText->setElideMode(QQuickText::ElideNone);
2876     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2877
2878     myText->setFontSizeMode(QQuickText::VerticalFit);
2879     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2880     // VerticalFit should reduce the size to the wrapped text within the vertical height.
2881     verticalFitHeight = myText->contentHeight();
2882     qreal verticalFitWidth = myText->contentWidth();
2883     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2884     QVERIFY(verticalFitHeight <= myText->height() + 2);
2885     QVERIFY(verticalFitHeight < originalHeight);
2886
2887     // Elide won't affect the height or width of a wrapped text with VerticalFit.
2888     myText->setElideMode(QQuickText::ElideRight);
2889     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2890     QVERIFY(!myText->truncated());
2891     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2892     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2893
2894     myText->setElideMode(QQuickText::ElideNone);
2895     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2896
2897     myText->setFontSizeMode(QQuickText::Fit);
2898     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2899     // Should be the same as VerticalFit with wrapping.
2900     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2901     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2902
2903     // Elide won't affect the size with Fit.
2904     myText->setElideMode(QQuickText::ElideRight);
2905     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2906     QVERIFY(!myText->truncated());
2907     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2908     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2909
2910     myText->setElideMode(QQuickText::ElideNone);
2911     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2912
2913     myText->setFontSizeMode(QQuickText::FixedSize);
2914     myText->setMaximumLineCount(2);
2915     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2916
2917     // The original text wrapped should exceed the height of the item.
2918     QVERIFY(originalWidth <= myText->width() + 2);
2919     QVERIFY(originalHeight > myText->height());
2920
2921     myText->setFontSizeMode(QQuickText::HorizontalFit);
2922     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2923     // HorizontalFit should reduce the font size to minimize wrapping, which brings it back to the
2924     // same size as without text wrapping.
2925     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2926     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2927
2928     // Elide won't affect the size with HorizontalFit.
2929     myText->setElideMode(QQuickText::ElideRight);
2930     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2931     QVERIFY(!myText->truncated());
2932     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2933     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2934
2935     myText->setElideMode(QQuickText::ElideNone);
2936     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2937
2938     myText->setFontSizeMode(QQuickText::VerticalFit);
2939     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2940     // VerticalFit should reduce the size to the wrapped text within the vertical height.
2941     verticalFitHeight = myText->contentHeight();
2942     verticalFitWidth = myText->contentWidth();
2943     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2944     QVERIFY(verticalFitHeight <= myText->height() + 2);
2945     QVERIFY(verticalFitHeight < originalHeight);
2946
2947     // Elide won't affect the height or width of a wrapped text with VerticalFit.
2948     myText->setElideMode(QQuickText::ElideRight);
2949     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2950     QVERIFY(!myText->truncated());
2951     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2952     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2953
2954     myText->setElideMode(QQuickText::ElideNone);
2955     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2956
2957     myText->setFontSizeMode(QQuickText::Fit);
2958     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2959     // Should be the same as VerticalFit with wrapping.
2960     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2961     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2962
2963     // Elide won't affect the size with Fit.
2964     myText->setElideMode(QQuickText::ElideRight);
2965     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2966     QVERIFY(!myText->truncated());
2967     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2968     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2969
2970     myText->setElideMode(QQuickText::ElideNone);
2971     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2972 }
2973
2974 void tst_qquicktext::fontSizeModeMultiline_data()
2975 {
2976     QTest::addColumn<QString>("text");
2977     QTest::newRow("plain") << "The quick red fox jumped\n over the lazy brown dog";
2978     QTest::newRow("styledtext") << "<b>The quick red fox jumped<br/> over the lazy brown dog</b>";
2979 }
2980
2981 void tst_qquicktext::fontSizeModeMultiline()
2982 {
2983     QFETCH(QString, text);
2984
2985     QScopedPointer<QQuickView> window(createView(testFile("fontSizeMode.qml")));
2986     window->show();
2987
2988     QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
2989     QVERIFY(myText != 0);
2990
2991     myText->setText(text);
2992     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2993
2994     qreal originalWidth = myText->contentWidth();
2995     qreal originalHeight = myText->contentHeight();
2996     QCOMPARE(myText->lineCount(), 2);
2997
2998     // The original text unwrapped should exceed the width and height of the item.
2999     QVERIFY(originalWidth > myText->width());
3000     QVERIFY(originalHeight > myText->height());
3001
3002     QFont font = myText->font();
3003     font.setPixelSize(64);
3004
3005     myText->setFont(font);
3006     myText->setFontSizeMode(QQuickText::HorizontalFit);
3007     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3008     // Font size reduced to fit within the width of the item.
3009     QCOMPARE(myText->lineCount(), 2);
3010     qreal horizontalFitWidth = myText->contentWidth();
3011     qreal horizontalFitHeight = myText->contentHeight();
3012     QVERIFY(horizontalFitWidth <= myText->width() + 2); // rounding
3013     QVERIFY(horizontalFitHeight > myText->height());
3014
3015     // Right eliding will remove the last line
3016     myText->setElideMode(QQuickText::ElideRight);
3017     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3018     QVERIFY(myText->truncated());
3019     QCOMPARE(myText->lineCount(), 1);
3020     QVERIFY(myText->contentWidth() <= myText->width() + 2);
3021     QVERIFY(myText->contentHeight() <= myText->height() + 2);
3022
3023     // Left or middle eliding wont have any effect.
3024     myText->setElideMode(QQuickText::ElideLeft);
3025     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3026     QVERIFY(!myText->truncated());
3027     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
3028     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
3029
3030     myText->setElideMode(QQuickText::ElideMiddle);
3031     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3032     QVERIFY(!myText->truncated());
3033     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
3034     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
3035
3036     myText->setElideMode(QQuickText::ElideNone);
3037     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3038
3039     myText->setFontSizeMode(QQuickText::VerticalFit);
3040     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3041     // Font size reduced to fit within the height of the item.
3042     qreal verticalFitWidth = myText->contentWidth();
3043     qreal verticalFitHeight = myText->contentHeight();
3044     QVERIFY(verticalFitWidth <= myText->width() + 2);
3045     QVERIFY(verticalFitHeight <= myText->height() + 2);
3046
3047     // Elide will have no effect.
3048     myText->setElideMode(QQuickText::ElideRight);
3049     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3050     QVERIFY(!myText->truncated());
3051     QVERIFY(myText->contentWidth() <= myText->width() + 2);
3052     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3053     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3054
3055     myText->setElideMode(QQuickText::ElideLeft);
3056     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3057     QVERIFY(!myText->truncated());
3058     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3059     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3060
3061     myText->setElideMode(QQuickText::ElideMiddle);
3062     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3063     QVERIFY(!myText->truncated());
3064     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3065     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3066
3067     myText->setElideMode(QQuickText::ElideNone);
3068     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3069
3070     myText->setFontSizeMode(QQuickText::Fit);
3071     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3072     // Should be the same as VerticalFit with no wrapping.
3073     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3074     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3075
3076     // Elide won't affect the size with Fit.
3077     myText->setElideMode(QQuickText::ElideRight);
3078     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3079     QVERIFY(!myText->truncated());
3080     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3081     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3082
3083     myText->setElideMode(QQuickText::ElideLeft);
3084     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3085     QVERIFY(!myText->truncated());
3086     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3087     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3088
3089     myText->setElideMode(QQuickText::ElideMiddle);
3090     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3091     QVERIFY(!myText->truncated());
3092     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3093     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3094
3095     myText->setElideMode(QQuickText::ElideNone);
3096     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3097
3098     myText->setFontSizeMode(QQuickText::FixedSize);
3099     myText->setWrapMode(QQuickText::Wrap);
3100     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3101
3102     originalWidth = myText->contentWidth();
3103     originalHeight = myText->contentHeight();
3104
3105     // The original text wrapped should exceed the height of the item.
3106     QVERIFY(originalWidth <= myText->width() + 2);
3107     QVERIFY(originalHeight > myText->height());
3108
3109     myText->setFontSizeMode(QQuickText::HorizontalFit);
3110     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3111     // HorizontalFit should reduce the font size to minimize wrapping, which brings it back to the
3112     // same size as without text wrapping.
3113     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
3114     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
3115
3116     // Text will be elided vertically with HorizontalFit
3117     myText->setElideMode(QQuickText::ElideRight);
3118     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3119     QVERIFY(myText->truncated());
3120     QVERIFY(myText->contentWidth() <= myText->width() + 2);
3121     QVERIFY(myText->contentHeight() <= myText->height() + 2);
3122
3123     myText->setElideMode(QQuickText::ElideNone);
3124     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3125
3126     myText->setFontSizeMode(QQuickText::VerticalFit);
3127     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3128     // VerticalFit should reduce the size to the wrapped text within the vertical height.
3129     verticalFitHeight = myText->contentHeight();
3130     verticalFitWidth = myText->contentWidth();
3131     QVERIFY(myText->contentWidth() <= myText->width() + 2);
3132     QVERIFY(verticalFitHeight <= myText->height() + 2);
3133     QVERIFY(verticalFitHeight < originalHeight);
3134
3135     // Elide won't affect the height or width of a wrapped text with VerticalFit.
3136     myText->setElideMode(QQuickText::ElideRight);
3137     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3138     QVERIFY(!myText->truncated());
3139     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3140     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3141
3142     myText->setElideMode(QQuickText::ElideNone);
3143     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3144
3145     myText->setFontSizeMode(QQuickText::Fit);
3146     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3147     // Should be the same as VerticalFit with wrapping.
3148     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3149     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3150
3151     // Elide won't affect the size with Fit.
3152     myText->setElideMode(QQuickText::ElideRight);
3153     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3154     QVERIFY(!myText->truncated());
3155     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3156     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3157
3158     myText->setElideMode(QQuickText::ElideNone);
3159     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3160
3161     myText->setFontSizeMode(QQuickText::FixedSize);
3162     myText->setMaximumLineCount(2);
3163     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3164
3165     // The original text wrapped should exceed the height of the item.
3166     QVERIFY(originalWidth <= myText->width() + 2);
3167     QVERIFY(originalHeight > myText->height());
3168
3169     myText->setFontSizeMode(QQuickText::HorizontalFit);
3170     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3171     // HorizontalFit should reduce the font size to minimize wrapping, which brings it back to the
3172     // same size as without text wrapping.
3173     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
3174     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
3175
3176     // Elide won't affect the size with HorizontalFit.
3177     myText->setElideMode(QQuickText::ElideRight);
3178     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3179     QVERIFY(myText->truncated());
3180     QVERIFY(myText->contentWidth() <= myText->width() + 2);
3181     QVERIFY(myText->contentHeight() <= myText->height() + 2);
3182
3183     myText->setElideMode(QQuickText::ElideNone);
3184     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3185
3186     myText->setFontSizeMode(QQuickText::VerticalFit);
3187     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3188     // VerticalFit should reduce the size to the wrapped text within the vertical height.
3189     verticalFitHeight = myText->contentHeight();
3190     verticalFitWidth = myText->contentWidth();
3191     QVERIFY(myText->contentWidth() <= myText->width() + 2);
3192     QVERIFY(verticalFitHeight <= myText->height() + 2);
3193     QVERIFY(verticalFitHeight < originalHeight);
3194
3195     // Elide won't affect the height or width of a wrapped text with VerticalFit.
3196     myText->setElideMode(QQuickText::ElideRight);
3197     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3198     QVERIFY(!myText->truncated());
3199     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3200     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3201
3202     myText->setElideMode(QQuickText::ElideNone);
3203     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3204
3205     myText->setFontSizeMode(QQuickText::Fit);
3206     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3207     // Should be the same as VerticalFit with wrapping.
3208     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3209     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3210
3211     // Elide won't affect the size with Fit.
3212     myText->setElideMode(QQuickText::ElideRight);
3213     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3214     QVERIFY(!myText->truncated());
3215     QCOMPARE(myText->contentWidth(), verticalFitWidth);
3216     QCOMPARE(myText->contentHeight(), verticalFitHeight);
3217
3218     myText->setElideMode(QQuickText::ElideNone);
3219     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3220 }
3221
3222 void tst_qquicktext::multilengthStrings_data()
3223 {
3224     QTest::addColumn<QString>("source");
3225     QTest::newRow("No Wrap") << testFile("multilengthStrings.qml");
3226     QTest::newRow("Wrap") << testFile("multilengthStringsWrapped.qml");
3227 }
3228
3229 void tst_qquicktext::multilengthStrings()
3230 {
3231     QFETCH(QString, source);
3232
3233     QScopedPointer<QQuickView> window(createView(source));
3234     window->show();
3235
3236     QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
3237     QVERIFY(myText != 0);
3238
3239     const QString longText = "the quick brown fox jumped over the lazy dog";
3240     const QString mediumText = "the brown fox jumped over the dog";
3241     const QString shortText = "fox jumped dog";
3242
3243     myText->setText(longText);
3244     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3245     const qreal longWidth = myText->contentWidth();
3246     const qreal longHeight = myText->contentHeight();
3247
3248     myText->setText(mediumText);
3249     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3250     const qreal mediumWidth = myText->contentWidth();
3251     const qreal mediumHeight = myText->contentHeight();
3252
3253     myText->setText(shortText);
3254     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3255     const qreal shortWidth = myText->contentWidth();
3256     const qreal shortHeight = myText->contentHeight();
3257
3258     myText->setElideMode(QQuickText::ElideRight);
3259     myText->setText(longText + QLatin1Char('\x9c') + mediumText + QLatin1Char('\x9c') + shortText);
3260
3261     myText->setSize(QSizeF(longWidth, longHeight));
3262     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3263
3264     QCOMPARE(myText->contentWidth(), longWidth);
3265     QCOMPARE(myText->contentHeight(), longHeight);
3266     QCOMPARE(myText->truncated(), false);
3267
3268     myText->setSize(QSizeF(mediumWidth, mediumHeight));
3269     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3270
3271     QCOMPARE(myText->contentWidth(), mediumWidth);
3272     QCOMPARE(myText->contentHeight(), mediumHeight);
3273     QCOMPARE(myText->truncated(), true);
3274
3275     myText->setSize(QSizeF(shortWidth, shortHeight));
3276     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
3277
3278     QCOMPARE(myText->contentWidth(), shortWidth);
3279     QCOMPARE(myText->contentHeight(), shortHeight);
3280     QCOMPARE(myText->truncated(), true);
3281 }
3282
3283 void tst_qquicktext::fontFormatSizes_data()
3284 {
3285     QTest::addColumn<QString>("text");
3286     QTest::addColumn<QString>("textWithTag");
3287     QTest::addColumn<bool>("fontIsBigger");
3288
3289     QTest::newRow("fs1") << "Hello world!" << "Hello <font size=\"1\">world</font>!" << false;
3290     QTest::newRow("fs2") << "Hello world!" << "Hello <font size=\"2\">world</font>!" << false;
3291     QTest::newRow("fs3") << "Hello world!" << "Hello <font size=\"3\">world</font>!" << false;
3292     QTest::newRow("fs4") << "Hello world!" << "Hello <font size=\"4\">world</font>!" << true;
3293     QTest::newRow("fs5") << "Hello world!" << "Hello <font size=\"5\">world</font>!" << true;
3294     QTest::newRow("fs6") << "Hello world!" << "Hello <font size=\"6\">world</font>!" << true;
3295     QTest::newRow("fs7") << "Hello world!" << "Hello <font size=\"7\">world</font>!" << true;
3296     QTest::newRow("h1") << "This is<br/>a font<br/> size test." << "This is <h1>a font</h1> size test." << true;
3297     QTest::newRow("h2") << "This is<br/>a font<br/> size test." << "This is <h2>a font</h2> size test." << true;
3298     QTest::newRow("h3") << "This is<br/>a font<br/> size test." << "This is <h3>a font</h3> size test." << true;
3299     QTest::newRow("h4") << "This is<br/>a font<br/> size test." << "This is <h4>a font</h4> size test." << true;
3300     QTest::newRow("h5") << "This is<br/>a font<br/> size test." << "This is <h5>a font</h5> size test." << false;
3301     QTest::newRow("h6") << "This is<br/>a font<br/> size test." << "This is <h6>a font</h6> size test." << false;
3302 }
3303
3304 void tst_qquicktext::fontFormatSizes()
3305 {
3306     QFETCH(QString, text);
3307     QFETCH(QString, textWithTag);
3308     QFETCH(bool, fontIsBigger);
3309
3310     QQuickView *view = new QQuickView;
3311     {
3312         view->setSource(testFileUrl("pointFontSizes.qml"));
3313         view->show();
3314
3315         QQuickText *qtext = view->rootObject()->findChild<QQuickText*>("text");
3316         QQuickText *qtextWithTag = view->rootObject()->findChild<QQuickText*>("textWithTag");
3317         QVERIFY(qtext != 0);
3318         QVERIFY(qtextWithTag != 0);
3319
3320         qtext->setText(text);
3321         qtextWithTag->setText(textWithTag);
3322
3323         for (int size = 6; size < 100; size += 4) {
3324             view->rootObject()->setProperty("pointSize", size);
3325             if (fontIsBigger)
3326                 QVERIFY(qtext->height() <= qtextWithTag->height());
3327             else
3328                 QVERIFY(qtext->height() >= qtextWithTag->height());
3329         }
3330     }
3331
3332     {
3333         view->setSource(testFileUrl("pixelFontSizes.qml"));
3334         QQuickText *qtext = view->rootObject()->findChild<QQuickText*>("text");
3335         QQuickText *qtextWithTag = view->rootObject()->findChild<QQuickText*>("textWithTag");
3336         QVERIFY(qtext != 0);
3337         QVERIFY(qtextWithTag != 0);
3338
3339         qtext->setText(text);
3340         qtextWithTag->setText(textWithTag);
3341
3342         for (int size = 6; size < 100; size += 4) {
3343             view->rootObject()->setProperty("pixelSize", size);
3344             if (fontIsBigger)
3345                 QVERIFY(qtext->height() <= qtextWithTag->height());
3346             else
3347                 QVERIFY(qtext->height() >= qtextWithTag->height());
3348         }
3349     }
3350     delete view;
3351 }
3352
3353 typedef qreal (*ExpectedBaseline)(QQuickText *item);
3354 Q_DECLARE_METATYPE(ExpectedBaseline)
3355
3356 static qreal expectedBaselineTop(QQuickText *item)
3357 {
3358     QFontMetricsF fm(item->font());
3359     return fm.ascent();
3360 }
3361
3362 static qreal expectedBaselineBottom(QQuickText *item)
3363 {
3364     QFontMetricsF fm(item->font());
3365     return item->height() - item->contentHeight() + fm.ascent();
3366 }
3367
3368 static qreal expectedBaselineCenter(QQuickText *item)
3369 {
3370     QFontMetricsF fm(item->font());
3371     return ((item->height() - item->contentHeight()) / 2) + fm.ascent();
3372 }
3373
3374 static qreal expectedBaselineBold(QQuickText *item)
3375 {
3376     QFont font = item->font();
3377     font.setBold(true);
3378     QFontMetricsF fm(font);
3379     return fm.ascent();
3380 }
3381
3382 static qreal expectedBaselineImage(QQuickText *item)
3383 {
3384     QFontMetricsF fm(item->font());
3385     // The line is positioned so the bottom of the line is aligned with the bottom of the image,
3386     // or image height - line height and the baseline is line position + ascent.  Because
3387     // QTextLine's height is rounded up this can give slightly different results to image height
3388     // - descent.
3389     return 181 - qCeil(fm.height()) + fm.ascent();
3390 }
3391
3392 static qreal expectedBaselineCustom(QQuickText *item)
3393 {
3394     QFontMetricsF fm(item->font());
3395     return 16 + fm.ascent();
3396 }
3397
3398 static qreal expectedBaselineScaled(QQuickText *item)
3399 {
3400     QFont font = item->font();
3401     QTextLayout layout(item->text().replace(QLatin1Char('\n'), QChar::LineSeparator));
3402     do {
3403         layout.setFont(font);
3404         qreal width = 0;
3405         layout.beginLayout();
3406         for (QTextLine line = layout.createLine(); line.isValid(); line = layout.createLine()) {
3407             line.setLineWidth(FLT_MAX);
3408             width = qMax(line.naturalTextWidth(), width);
3409         }
3410         layout.endLayout();
3411
3412         if (width < item->width()) {
3413             QFontMetricsF fm(layout.font());
3414             return fm.ascent();
3415         }
3416         font.setPointSize(font.pointSize() - 1);
3417     } while (font.pointSize() > 0);
3418     return 0;
3419 }
3420
3421 static qreal expectedBaselineFixedBottom(QQuickText *item)
3422 {
3423     QFontMetricsF fm(item->font());
3424     qreal dy = item->text().contains(QLatin1Char('\n'))
3425             ? 160
3426             : 180;
3427     return dy + fm.ascent();
3428 }
3429
3430 static qreal expectedBaselineProportionalBottom(QQuickText *item)
3431 {
3432     QFontMetricsF fm(item->font());
3433     qreal dy = item->text().contains(QLatin1Char('\n'))
3434             ? 200 - (qCeil(fm.height()) * 3)
3435             : 200 - (qCeil(fm.height()) * 1.5);
3436     return dy + fm.ascent();
3437 }
3438
3439 void tst_qquicktext::baselineOffset_data()
3440 {
3441     qRegisterMetaType<ExpectedBaseline>();
3442     QTest::addColumn<QString>("text");
3443     QTest::addColumn<QString>("wrappedText");
3444     QTest::addColumn<QByteArray>("bindings");
3445     QTest::addColumn<ExpectedBaseline>("expectedBaseline");
3446     QTest::addColumn<ExpectedBaseline>("expectedBaselineEmpty");
3447
3448     QTest::newRow("top align")
3449             << "hello world"
3450             << "hello\nworld"
3451             << QByteArray("height: 200; verticalAlignment: Text.AlignTop")
3452             << &expectedBaselineTop
3453             << &expectedBaselineTop;
3454     QTest::newRow("bottom align")
3455             << "hello world"
3456             << "hello\nworld"
3457             << QByteArray("height: 200; verticalAlignment: Text.AlignBottom")
3458             << &expectedBaselineBottom
3459             << &expectedBaselineBottom;
3460     QTest::newRow("center align")
3461             << "hello world"
3462             << "hello\nworld"
3463             << QByteArray("height: 200; verticalAlignment: Text.AlignVCenter")
3464             << &expectedBaselineCenter
3465             << &expectedBaselineCenter;
3466
3467     QTest::newRow("bold")
3468             << "<b>hello world</b>"
3469             << "<b>hello<br/>world</b>"
3470             << QByteArray("height: 200")
3471             << &expectedBaselineTop
3472             << &expectedBaselineBold;
3473
3474     QTest::newRow("richText")
3475             << "<b>hello world</b>"
3476             << "<b>hello<br/>world</b>"
3477             << QByteArray("height: 200; textFormat: Text.RichText")
3478             << &expectedBaselineTop
3479             << &expectedBaselineTop;
3480
3481     QTest::newRow("elided")
3482             << "hello world"
3483             << "hello\nworld"
3484             << QByteArray("width: 20; height: 8; elide: Text.ElideRight")
3485             << &expectedBaselineTop
3486             << &expectedBaselineTop;
3487
3488     QTest::newRow("elided bottom align")
3489             << "hello world"
3490             << "hello\nworld!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
3491             << QByteArray("width: 200; height: 200; elide: Text.ElideRight; verticalAlignment: Text.AlignBottom")
3492             << &expectedBaselineBottom
3493             << &expectedBaselineBottom;
3494
3495     QTest::newRow("image")
3496             << "hello <img src=\"images/heart200.png\" /> world"
3497             << "hello <img src=\"images/heart200.png\" /><br/>world"
3498             << QByteArray("height: 200\n; baseUrl: \"") + testFileUrl("reference").toEncoded() + QByteArray("\"")
3499             << &expectedBaselineImage
3500             << &expectedBaselineTop;
3501
3502     QTest::newRow("customLine")
3503             << "hello world"
3504             << "hello\nworld"
3505             << QByteArray("height: 200; onLineLaidOut: line.y += 16")
3506             << &expectedBaselineCustom
3507             << &expectedBaselineCustom;
3508
3509     QTest::newRow("scaled font")
3510             << "hello world"
3511             << "hello\nworld"
3512             << QByteArray("width: 200; minimumPointSize: 1; font.pointSize: 64; fontSizeMode: Text.HorizontalFit")
3513             << &expectedBaselineScaled
3514             << &expectedBaselineTop;
3515
3516     QTest::newRow("fixed line height top align")
3517             << "hello world"
3518             << "hello\nworld"
3519             << QByteArray("height: 200; lineHeightMode: Text.FixedHeight; lineHeight: 20; verticalAlignment: Text.AlignTop")
3520             << &expectedBaselineTop
3521             << &expectedBaselineTop;
3522
3523     QTest::newRow("fixed line height bottom align")
3524             << "hello world"
3525             << "hello\nworld"
3526             << QByteArray("height: 200; lineHeightMode: Text.FixedHeight; lineHeight: 20; verticalAlignment: Text.AlignBottom")
3527             << &expectedBaselineFixedBottom
3528             << &expectedBaselineFixedBottom;
3529
3530     QTest::newRow("proportional line height top align")
3531             << "hello world"
3532             << "hello\nworld"
3533             << QByteArray("height: 200; lineHeightMode: Text.ProportionalHeight; lineHeight: 1.5; verticalAlignment: Text.AlignTop")
3534             << &expectedBaselineTop
3535             << &expectedBaselineTop;
3536
3537     QTest::newRow("proportional line height bottom align")
3538             << "hello world"
3539             << "hello\nworld"
3540             << QByteArray("height: 200; lineHeightMode: Text.ProportionalHeight; lineHeight: 1.5; verticalAlignment: Text.AlignBottom")
3541             << &expectedBaselineProportionalBottom
3542             << &expectedBaselineProportionalBottom;
3543 }
3544
3545 void tst_qquicktext::baselineOffset()
3546 {
3547     QFETCH(QString, text);
3548     QFETCH(QString, wrappedText);
3549     QFETCH(QByteArray, bindings);
3550     QFETCH(ExpectedBaseline, expectedBaseline);
3551     QFETCH(ExpectedBaseline, expectedBaselineEmpty);
3552
3553     QQmlComponent component(&engine);
3554     component.setData(
3555             "import QtQuick 2.0\n"
3556             "Text {\n"
3557                 + bindings + "\n"
3558             "}", QUrl());
3559
3560     QScopedPointer<QObject> object(component.create());
3561
3562     QQuickText *item = qobject_cast<QQuickText *>(object.data());
3563     QVERIFY(item);
3564
3565     {
3566         qreal baseline = expectedBaselineEmpty(item);
3567
3568         QCOMPARE(item->baselineOffset(), baseline);
3569
3570         item->setText(text);
3571         if (expectedBaseline != expectedBaselineEmpty)
3572             baseline = expectedBaseline(item);
3573
3574         QCOMPARE(item->baselineOffset(), baseline);
3575
3576         item->setText(wrappedText);
3577         QCOMPARE(item->baselineOffset(), expectedBaseline(item));
3578     }
3579
3580     QFont font = item->font();
3581     font.setPointSize(font.pointSize() + 8);
3582
3583     {
3584         QCOMPARE(item->baselineOffset(), expectedBaseline(item));
3585
3586         item->setText(text);
3587         qreal baseline = expectedBaseline(item);
3588         QCOMPARE(item->baselineOffset(), baseline);
3589
3590         item->setText(QString());
3591         if (expectedBaselineEmpty != expectedBaseline)
3592             baseline = expectedBaselineEmpty(item);
3593
3594         QCOMPARE(item->baselineOffset(), baseline);
3595     }
3596 }
3597
3598 void tst_qquicktext::htmlLists()
3599 {
3600     QFETCH(QString, text);
3601     QFETCH(int, nbLines);
3602
3603     QQuickView *view = createView(testFile("htmlLists.qml"));
3604     QQuickText *textObject = view->rootObject()->findChild<QQuickText*>("myText");
3605
3606     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
3607     QVERIFY(textPrivate != 0);
3608     QVERIFY(textPrivate->extra.isAllocated());
3609
3610     QVERIFY(textObject != 0);
3611     textObject->setText(text);
3612
3613     view->show();
3614     view->requestActivateWindow();
3615     QVERIFY(QTest::qWaitForWindowActive(view));
3616
3617     QCOMPARE(textPrivate->extra->doc->lineCount(), nbLines);
3618
3619     delete view;
3620 }
3621
3622 void tst_qquicktext::htmlLists_data()
3623 {
3624     QTest::addColumn<QString>("text");
3625     QTest::addColumn<int>("nbLines");
3626
3627     QTest::newRow("ordered list") << "<ol><li>one<li>two<li>three" << 3;
3628     QTest::newRow("ordered list closed") << "<ol><li>one</li></ol>" << 1;
3629     QTest::newRow("ordered list alpha") << "<ol type=\"a\"><li>one</li><li>two</li></ol>" << 2;
3630     QTest::newRow("ordered list upper alpha") << "<ol type=\"A\"><li>one</li><li>two</li></ol>" << 2;
3631     QTest::newRow("ordered list roman") << "<ol type=\"i\"><li>one</li><li>two</li></ol>" << 2;
3632     QTest::newRow("ordered list upper roman") << "<ol type=\"I\"><li>one</li><li>two</li></ol>" << 2;
3633     QTest::newRow("ordered list bad") << "<ol type=\"z\"><li>one</li><li>two</li></ol>" << 2;
3634     QTest::newRow("unordered list") << "<ul><li>one<li>two" << 2;
3635     QTest::newRow("unordered list closed") << "<ul><li>one</li><li>two</li></ul>" << 2;
3636     QTest::newRow("unordered list disc") << "<ul type=\"disc\"><li>one</li><li>two</li></ul>" << 2;
3637     QTest::newRow("unordered list square") << "<ul type=\"square\"><li>one</li><li>two</li></ul>" << 2;
3638     QTest::newRow("unordered list bad") << "<ul type=\"bad\"><li>one</li><li>two</li></ul>" << 2;
3639 }
3640
3641 QTEST_MAIN(tst_qquicktext)
3642
3643 #include "tst_qquicktext.moc"