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