Fix bounding rects of text items.
[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 Q_DECLARE_METATYPE(QQuickText::TextFormat)
61
62 class tst_qquicktext : public QQmlDataTest
63 {
64     Q_OBJECT
65 public:
66     tst_qquicktext();
67
68 private slots:
69     void text();
70     void width();
71     void wrap();
72     void elide();
73     void multilineElide_data();
74     void multilineElide();
75     void implicitElide_data();
76     void implicitElide();
77     void textFormat();
78
79     void alignments_data();
80     void alignments();
81
82     void baseUrl();
83     void embeddedImages_data();
84     void embeddedImages();
85
86     void lineCount();
87     void lineHeight();
88
89     // ### these tests may be trivial
90     void horizontalAlignment();
91     void horizontalAlignment_RightToLeft();
92     void verticalAlignment();
93     void font();
94     void style();
95     void color();
96     void smooth();
97
98     // QQuickFontValueType
99     void weight();
100     void underline();
101     void overline();
102     void strikeout();
103     void capitalization();
104     void letterSpacing();
105     void wordSpacing();
106
107     void clickLink();
108
109     void implicitSize_data();
110     void implicitSize();
111     void contentSize();
112     void implicitSizeBinding_data();
113     void implicitSizeBinding();
114
115     void boundingRect_data();
116     void boundingRect();
117     void clipRect();
118     void lineLaidOut();
119
120     void imgTagsBaseUrl_data();
121     void imgTagsBaseUrl();
122     void imgTagsAlign_data();
123     void imgTagsAlign();
124     void imgTagsMultipleImages();
125     void imgTagsElide();
126     void imgTagsUpdates();
127     void imgTagsError();
128     void fontSizeMode_data();
129     void fontSizeMode();
130     void fontSizeModeMultiline_data();
131     void fontSizeModeMultiline();
132     void multilengthStrings_data();
133     void multilengthStrings();
134     void fontFormatSizes_data();
135     void fontFormatSizes();
136
137     void baselineOffset_data();
138     void baselineOffset();
139
140 private:
141     QStringList standard;
142     QStringList richText;
143
144     QStringList horizontalAlignmentmentStrings;
145     QStringList verticalAlignmentmentStrings;
146
147     QList<Qt::Alignment> verticalAlignmentments;
148     QList<Qt::Alignment> horizontalAlignmentments;
149
150     QStringList styleStrings;
151     QList<QQuickText::TextStyle> styles;
152
153     QStringList colorStrings;
154
155     QQmlEngine engine;
156
157     QQuickView *createView(const QString &filename);
158 };
159
160 tst_qquicktext::tst_qquicktext()
161 {
162     standard << "the quick brown fox jumped over the lazy dog"
163             << "the quick brown fox\n jumped over the lazy dog";
164
165     richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
166             << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
167
168     horizontalAlignmentmentStrings << "AlignLeft"
169             << "AlignRight"
170             << "AlignHCenter";
171
172     verticalAlignmentmentStrings << "AlignTop"
173             << "AlignBottom"
174             << "AlignVCenter";
175
176     horizontalAlignmentments << Qt::AlignLeft
177             << Qt::AlignRight
178             << Qt::AlignHCenter;
179
180     verticalAlignmentments << Qt::AlignTop
181             << Qt::AlignBottom
182             << Qt::AlignVCenter;
183
184     styleStrings << "Normal"
185             << "Outline"
186             << "Raised"
187             << "Sunken";
188
189     styles << QQuickText::Normal
190             << QQuickText::Outline
191             << QQuickText::Raised
192             << QQuickText::Sunken;
193
194     colorStrings << "aliceblue"
195             << "antiquewhite"
196             << "aqua"
197             << "darkkhaki"
198             << "darkolivegreen"
199             << "dimgray"
200             << "palevioletred"
201             << "lightsteelblue"
202             << "#000000"
203             << "#AAAAAA"
204             << "#FFFFFF"
205             << "#2AC05F";
206     //
207     // need a different test to do alpha channel test
208     // << "#AA0011DD"
209     // << "#00F16B11";
210     //
211 }
212
213 QQuickView *tst_qquicktext::createView(const QString &filename)
214 {
215     QQuickView *canvas = new QQuickView(0);
216
217     canvas->setSource(QUrl::fromLocalFile(filename));
218     return canvas;
219 }
220
221 void tst_qquicktext::text()
222 {
223     {
224         QQmlComponent textComponent(&engine);
225         textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
226         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
227
228         QVERIFY(textObject != 0);
229         QCOMPARE(textObject->text(), QString(""));
230         QVERIFY(textObject->width() == 0);
231
232         delete textObject;
233     }
234
235     for (int i = 0; i < standard.size(); i++)
236     {
237         QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
238         QQmlComponent textComponent(&engine);
239         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
240
241         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
242
243         QVERIFY(textObject != 0);
244         QCOMPARE(textObject->text(), standard.at(i));
245         QVERIFY(textObject->width() > 0);
246
247         delete textObject;
248     }
249
250     for (int i = 0; i < richText.size(); i++)
251     {
252         QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
253         QQmlComponent textComponent(&engine);
254         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
255         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
256
257         QVERIFY(textObject != 0);
258         QString expected = richText.at(i);
259         QCOMPARE(textObject->text(), expected.replace("\\\"", "\""));
260         QVERIFY(textObject->width() > 0);
261
262         delete textObject;
263     }
264 }
265
266 void tst_qquicktext::width()
267 {
268     // uses Font metrics to find the width for standard and document to find the width for rich
269     {
270         QQmlComponent textComponent(&engine);
271         textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
272         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
273
274         QVERIFY(textObject != 0);
275         QCOMPARE(textObject->width(), 0.);
276
277         delete textObject;
278     }
279
280     bool requiresUnhintedMetrics = !qmlDisableDistanceField();
281
282     for (int i = 0; i < standard.size(); i++)
283     {
284         QVERIFY(!Qt::mightBeRichText(standard.at(i))); // self-test
285
286         QFont f;
287         qreal metricWidth = 0.0;
288
289         if (requiresUnhintedMetrics) {
290             QString s = standard.at(i);
291             s.replace(QLatin1Char('\n'), QChar::LineSeparator);
292
293             QTextLayout layout(s);
294             layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
295             {
296                 QTextOption option;
297                 option.setUseDesignMetrics(true);
298                 layout.setTextOption(option);
299             }
300
301             layout.beginLayout();
302             forever {
303                 QTextLine line = layout.createLine();
304                 if (!line.isValid())
305                     break;
306             }
307
308             layout.endLayout();
309
310             metricWidth = layout.boundingRect().width();
311         } else {
312             QFontMetricsF fm(f);
313             metricWidth = fm.size(Qt::TextExpandTabs && Qt::TextShowMnemonic, standard.at(i)).width();
314         }
315
316         QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
317         QQmlComponent textComponent(&engine);
318         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
319         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
320
321         QVERIFY(textObject != 0);
322         QVERIFY(textObject->boundingRect().width() > 0);
323         QCOMPARE(textObject->width(), qreal(metricWidth));
324         QVERIFY(textObject->textFormat() == QQuickText::AutoText); // setting text doesn't change format
325
326         delete textObject;
327     }
328
329     for (int i = 0; i < richText.size(); i++)
330     {
331         QVERIFY(Qt::mightBeRichText(richText.at(i))); // self-test
332
333         QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\"; textFormat: Text.RichText }";
334         QQmlComponent textComponent(&engine);
335         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
336         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
337         QVERIFY(textObject != 0);
338
339         QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
340         QVERIFY(textPrivate != 0);
341         QVERIFY(textPrivate->extra.isAllocated());
342
343         QTextDocument *doc = textPrivate->extra->doc;
344         QVERIFY(doc != 0);
345
346         QCOMPARE(int(textObject->width()), int(doc->idealWidth()));
347         QVERIFY(textObject->textFormat() == QQuickText::RichText);
348
349         delete textObject;
350     }
351 }
352
353 void tst_qquicktext::wrap()
354 {
355     int textHeight = 0;
356     // for specified width and wrap set true
357     {
358         QQmlComponent textComponent(&engine);
359         textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; wrapMode: Text.WordWrap; width: 300 }", QUrl::fromLocalFile(""));
360         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
361         textHeight = textObject->height();
362
363         QVERIFY(textObject != 0);
364         QVERIFY(textObject->wrapMode() == QQuickText::WordWrap);
365         QCOMPARE(textObject->width(), 300.);
366
367         delete textObject;
368     }
369
370     for (int i = 0; i < standard.size(); i++)
371     {
372         QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + standard.at(i) + "\" }";
373         QQmlComponent textComponent(&engine);
374         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
375         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
376
377         QVERIFY(textObject != 0);
378         QCOMPARE(textObject->width(), 30.);
379         QVERIFY(textObject->height() > textHeight);
380
381         int oldHeight = textObject->height();
382         textObject->setWidth(100);
383         QVERIFY(textObject->height() < oldHeight);
384
385         delete textObject;
386     }
387
388     for (int i = 0; i < richText.size(); i++)
389     {
390         QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + richText.at(i) + "\" }";
391         QQmlComponent textComponent(&engine);
392         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
393         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
394
395         QVERIFY(textObject != 0);
396         QCOMPARE(textObject->width(), 30.);
397         QVERIFY(textObject->height() > textHeight);
398
399         qreal oldHeight = textObject->height();
400         textObject->setWidth(100);
401         QVERIFY(textObject->height() < oldHeight);
402
403         delete textObject;
404     }
405
406     // richtext again with a fixed height
407     for (int i = 0; i < richText.size(); i++)
408     {
409         QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; height: 50; text: \"" + richText.at(i) + "\" }";
410         QQmlComponent textComponent(&engine);
411         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
412         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
413
414         QVERIFY(textObject != 0);
415         QCOMPARE(textObject->width(), 30.);
416         QVERIFY(textObject->implicitHeight() > textHeight);
417
418         qreal oldHeight = textObject->implicitHeight();
419         textObject->setWidth(100);
420         QVERIFY(textObject->implicitHeight() < oldHeight);
421
422         delete textObject;
423     }
424 }
425
426 void tst_qquicktext::elide()
427 {
428     for (QQuickText::TextElideMode m = QQuickText::ElideLeft; m<=QQuickText::ElideNone; m=QQuickText::TextElideMode(int(m)+1)) {
429         const char* elidename[]={"ElideLeft", "ElideRight", "ElideMiddle", "ElideNone"};
430         QString elide = "elide: Text." + QString(elidename[int(m)]) + ";";
431
432         // XXX Poor coverage.
433
434         {
435             QQmlComponent textComponent(&engine);
436             textComponent.setData(("import QtQuick 2.0\nText { text: \"\"; "+elide+" width: 100 }").toLatin1(), QUrl::fromLocalFile(""));
437             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
438
439             QCOMPARE(textObject->elideMode(), m);
440             QCOMPARE(textObject->width(), 100.);
441
442             delete textObject;
443         }
444
445         for (int i = 0; i < standard.size(); i++)
446         {
447             QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + standard.at(i) + "\" }";
448             QQmlComponent textComponent(&engine);
449             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
450             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
451
452             QCOMPARE(textObject->elideMode(), m);
453             QCOMPARE(textObject->width(), 100.);
454
455             if (m != QQuickText::ElideNone && !standard.at(i).contains('\n'))
456                 QVERIFY(textObject->contentWidth() <= textObject->width());
457
458             delete textObject;
459         }
460
461         for (int i = 0; i < richText.size(); i++)
462         {
463             QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + richText.at(i) + "\" }";
464             QQmlComponent textComponent(&engine);
465             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
466             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
467
468             QCOMPARE(textObject->elideMode(), m);
469             QCOMPARE(textObject->width(), 100.);
470
471             if (m != QQuickText::ElideNone && standard.at(i).contains("<br>"))
472                 QVERIFY(textObject->contentWidth() <= textObject->width());
473
474             delete textObject;
475         }
476     }
477 }
478
479 void tst_qquicktext::multilineElide_data()
480 {
481     QTest::addColumn<QQuickText::TextFormat>("format");
482     QTest::newRow("plain") << QQuickText::PlainText;
483     QTest::newRow("styled") << QQuickText::StyledText;
484 }
485
486 void tst_qquicktext::multilineElide()
487 {
488     QFETCH(QQuickText::TextFormat, format);
489     QQuickView *canvas = createView(testFile("multilineelide.qml"));
490
491     QQuickText *myText = qobject_cast<QQuickText*>(canvas->rootObject());
492     QVERIFY(myText != 0);
493     myText->setTextFormat(format);
494
495     QCOMPARE(myText->lineCount(), 3);
496     QCOMPARE(myText->truncated(), true);
497
498     qreal lineHeight = myText->contentHeight() / 3.;
499
500     // Set a valid height greater than the truncated content height and ensure the line count is
501     // unchanged.
502     myText->setHeight(200);
503     QCOMPARE(myText->lineCount(), 3);
504     QCOMPARE(myText->truncated(), true);
505
506     // reduce size and ensure fewer lines are drawn
507     myText->setHeight(lineHeight * 2);
508     QCOMPARE(myText->lineCount(), 2);
509
510     myText->setHeight(lineHeight);
511     QCOMPARE(myText->lineCount(), 1);
512
513     myText->setHeight(5);
514     QCOMPARE(myText->lineCount(), 1);
515
516     myText->setHeight(lineHeight * 3);
517     QCOMPARE(myText->lineCount(), 3);
518
519     // remove max count and show all lines.
520     myText->setHeight(1000);
521     myText->resetMaximumLineCount();
522
523     QCOMPARE(myText->truncated(), false);
524
525     // reduce size again
526     myText->setHeight(lineHeight * 2);
527     QCOMPARE(myText->lineCount(), 2);
528     QCOMPARE(myText->truncated(), true);
529
530     // change line height
531     myText->setLineHeight(1.1);
532     QCOMPARE(myText->lineCount(), 1);
533
534     delete canvas;
535 }
536
537 void tst_qquicktext::implicitElide_data()
538 {
539     QTest::addColumn<QString>("width");
540     QTest::addColumn<QString>("initialText");
541     QTest::addColumn<QString>("text");
542
543     QTest::newRow("maximum width, empty")
544             << "Math.min(implicitWidth, 100)"
545             << "";
546     QTest::newRow("maximum width, short")
547             << "Math.min(implicitWidth, 100)"
548             << "the";
549     QTest::newRow("maximum width, long")
550             << "Math.min(implicitWidth, 100)"
551             << "the quick brown fox jumped over the lazy dog";
552     QTest::newRow("reset width, empty")
553             << "implicitWidth > 100 ? 100 : undefined"
554             << "";
555     QTest::newRow("reset width, short")
556             << "implicitWidth > 100 ? 100 : undefined"
557             << "the";
558     QTest::newRow("reset width, long")
559             << "implicitWidth > 100 ? 100 : undefined"
560             << "the quick brown fox jumped over the lazy dog";
561 }
562
563 void tst_qquicktext::implicitElide()
564 {
565     QFETCH(QString, width);
566     QFETCH(QString, initialText);
567
568     QString componentStr =
569             "import QtQuick 2.0\n"
570             "Text {\n"
571                 "width: " + width + "\n"
572                 "text: \"" + initialText + "\"\n"
573                 "elide: Text.ElideRight\n"
574             "}";
575     QQmlComponent textComponent(&engine);
576     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
577     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
578
579     QVERIFY(textObject->contentWidth() <= textObject->width());
580
581     textObject->setText("the quick brown fox jumped over");
582
583     QVERIFY(textObject->contentWidth() > 0);
584     QVERIFY(textObject->contentWidth() <= textObject->width());
585 }
586
587 void tst_qquicktext::textFormat()
588 {
589     {
590         QQmlComponent textComponent(&engine);
591         textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
592         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
593
594         QVERIFY(textObject != 0);
595         QVERIFY(textObject->textFormat() == QQuickText::RichText);
596
597         QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
598         QVERIFY(textPrivate != 0);
599         QVERIFY(textPrivate->richText == true);
600
601         delete textObject;
602     }
603     {
604         QQmlComponent textComponent(&engine);
605         textComponent.setData("import QtQuick 2.0\nText { text: \"<b>Hello</b>\" }", QUrl::fromLocalFile(""));
606         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
607
608         QVERIFY(textObject != 0);
609         QVERIFY(textObject->textFormat() == QQuickText::AutoText);
610
611         QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
612         QVERIFY(textPrivate != 0);
613         QVERIFY(textPrivate->styledText == true);
614
615         delete textObject;
616     }
617     {
618         QQmlComponent textComponent(&engine);
619         textComponent.setData("import QtQuick 2.0\nText { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
620         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
621
622         QVERIFY(textObject != 0);
623         QVERIFY(textObject->textFormat() == QQuickText::PlainText);
624
625         delete textObject;
626     }
627 }
628
629
630 void tst_qquicktext::alignments_data()
631 {
632     QTest::addColumn<int>("hAlign");
633     QTest::addColumn<int>("vAlign");
634     QTest::addColumn<QString>("expectfile");
635
636     QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << testFile("alignments_lt.png");
637     QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << testFile("alignments_rt.png");
638     QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << testFile("alignments_ct.png");
639
640     QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << testFile("alignments_lb.png");
641     QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << testFile("alignments_rb.png");
642     QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << testFile("alignments_cb.png");
643
644     QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << testFile("alignments_lc.png");
645     QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << testFile("alignments_rc.png");
646     QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << testFile("alignments_cc.png");
647 }
648
649
650 void tst_qquicktext::alignments()
651 {
652     QSKIP("Text alignment pixmap comparison tests will not work with scenegraph");
653 #if (0)// No widgets in scenegraph
654     QFETCH(int, hAlign);
655     QFETCH(int, vAlign);
656     QFETCH(QString, expectfile);
657
658     QQuickView *canvas = createView(testFile("alignments.qml"));
659     canvas->show();
660     canvas->requestActivateWindow();
661     QTest::qWait(50);
662     QTRY_COMPARE(QGuiApplication::activeWindow(), static_cast<QWidget *>(canvas));
663
664     QObject *ob = canvas->rootObject();
665     QVERIFY(ob != 0);
666     ob->setProperty("horizontalAlignment",hAlign);
667     ob->setProperty("verticalAlignment",vAlign);
668     QTRY_COMPARE(ob->property("running").toBool(),false);
669     QImage actual(canvas->width(), canvas->height(), QImage::Format_RGB32);
670     actual.fill(qRgb(255,255,255));
671     QPainter p(&actual);
672     canvas->render(&p);
673
674     QImage expect(expectfile);
675     if (QGuiApplicationPrivate::graphics_system_name == "raster" || QGuiApplicationPrivate::graphics_system_name == "") {
676         QCOMPARE(actual,expect);
677     }
678     delete canvas;
679 #endif
680 }
681
682 //the alignment tests may be trivial o.oa
683 void tst_qquicktext::horizontalAlignment()
684 {
685     //test one align each, and then test if two align fails.
686
687     for (int i = 0; i < standard.size(); i++)
688     {
689         for (int j=0; j < horizontalAlignmentmentStrings.size(); j++)
690         {
691             QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
692             QQmlComponent textComponent(&engine);
693             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
694             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
695
696             QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
697
698             delete textObject;
699         }
700     }
701
702     for (int i = 0; i < richText.size(); i++)
703     {
704         for (int j=0; j < horizontalAlignmentmentStrings.size(); j++)
705         {
706             QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
707             QQmlComponent textComponent(&engine);
708             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
709             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
710
711             QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
712
713             delete textObject;
714         }
715     }
716
717 }
718
719 void tst_qquicktext::horizontalAlignment_RightToLeft()
720 {
721     QQuickView *canvas = createView(testFile("horizontalAlignment_RightToLeft.qml"));
722     QQuickText *text = canvas->rootObject()->findChild<QQuickText*>("text");
723     QVERIFY(text != 0);
724     canvas->show();
725
726     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(text);
727     QVERIFY(textPrivate != 0);
728
729     QTRY_VERIFY(textPrivate->layout.lineCount());
730
731     // implicit alignment should follow the reading direction of RTL text
732     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
733     QCOMPARE(text->effectiveHAlign(), text->hAlign());
734     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
735
736     // explicitly left aligned text
737     text->setHAlign(QQuickText::AlignLeft);
738     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
739     QCOMPARE(text->effectiveHAlign(), text->hAlign());
740     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
741
742     // explicitly right aligned text
743     text->setHAlign(QQuickText::AlignRight);
744     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
745     QCOMPARE(text->effectiveHAlign(), text->hAlign());
746     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
747
748     // change to rich text
749     QString textString = text->text();
750     text->setText(QString("<i>") + textString + QString("</i>"));
751     text->setTextFormat(QQuickText::RichText);
752     text->resetHAlign();
753
754     // implicitly aligned rich text should follow the reading direction of text
755     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
756     QCOMPARE(text->effectiveHAlign(), text->hAlign());
757     QVERIFY(textPrivate->extra.isAllocated());
758     QVERIFY(textPrivate->extra->doc->defaultTextOption().alignment() & Qt::AlignLeft);
759
760     // explicitly left aligned rich text
761     text->setHAlign(QQuickText::AlignLeft);
762     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
763     QCOMPARE(text->effectiveHAlign(), text->hAlign());
764     QVERIFY(textPrivate->extra->doc->defaultTextOption().alignment() & Qt::AlignRight);
765
766     // explicitly right aligned rich text
767     text->setHAlign(QQuickText::AlignRight);
768     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
769     QCOMPARE(text->effectiveHAlign(), text->hAlign());
770     QVERIFY(textPrivate->extra->doc->defaultTextOption().alignment() & Qt::AlignLeft);
771
772     text->setText(textString);
773     text->setTextFormat(QQuickText::PlainText);
774
775     // explicitly center aligned
776     text->setHAlign(QQuickText::AlignHCenter);
777     QCOMPARE(text->hAlign(), QQuickText::AlignHCenter);
778     QCOMPARE(text->effectiveHAlign(), text->hAlign());
779     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
780     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().right() > canvas->width()/2);
781
782     // reseted alignment should go back to following the text reading direction
783     text->resetHAlign();
784     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
785     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
786
787     // mirror the text item
788     QQuickItemPrivate::get(text)->setLayoutMirror(true);
789
790     // mirrored implicit alignment should continue to follow the reading direction of the text
791     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
792     QCOMPARE(text->effectiveHAlign(), QQuickText::AlignRight);
793     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
794
795     // mirrored explicitly right aligned behaves as left aligned
796     text->setHAlign(QQuickText::AlignRight);
797     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
798     QCOMPARE(text->effectiveHAlign(), QQuickText::AlignLeft);
799     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
800
801     // mirrored explicitly left aligned behaves as right aligned
802     text->setHAlign(QQuickText::AlignLeft);
803     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
804     QCOMPARE(text->effectiveHAlign(), QQuickText::AlignRight);
805     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
806
807     // disable mirroring
808     QQuickItemPrivate::get(text)->setLayoutMirror(false);
809     text->resetHAlign();
810
811     // English text should be implicitly left aligned
812     text->setText("Hello world!");
813     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
814     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
815
816     // empty text with implicit alignment follows the system locale-based
817     // keyboard input direction from QInputMethod::inputDirection()
818     text->setText("");
819     QCOMPARE(text->hAlign(), qApp->inputMethod()->inputDirection() == Qt::LeftToRight ?
820                                   QQuickText::AlignLeft : QQuickText::AlignRight);
821     text->setHAlign(QQuickText::AlignRight);
822     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
823
824     delete canvas;
825
826     // alignment of Text with no text set to it
827     QString componentStr = "import QtQuick 2.0\nText {}";
828     QQmlComponent textComponent(&engine);
829     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
830     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
831     QCOMPARE(textObject->hAlign(), qApp->inputMethod()->inputDirection() == Qt::LeftToRight ?
832                                   QQuickText::AlignLeft : QQuickText::AlignRight);
833     delete textObject;
834 }
835
836 void tst_qquicktext::verticalAlignment()
837 {
838     //test one align each, and then test if two align fails.
839
840     for (int i = 0; i < standard.size(); i++)
841     {
842         for (int j=0; j < verticalAlignmentmentStrings.size(); j++)
843         {
844             QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
845             QQmlComponent textComponent(&engine);
846             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
847             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
848
849             QVERIFY(textObject != 0);
850             QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
851
852             delete textObject;
853         }
854     }
855
856     for (int i = 0; i < richText.size(); i++)
857     {
858         for (int j=0; j < verticalAlignmentmentStrings.size(); j++)
859         {
860             QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
861             QQmlComponent textComponent(&engine);
862             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
863             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
864
865             QVERIFY(textObject != 0);
866             QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
867
868             delete textObject;
869         }
870     }
871
872 }
873
874 void tst_qquicktext::font()
875 {
876     //test size, then bold, then italic, then family
877     {
878         QString componentStr = "import QtQuick 2.0\nText { font.pointSize: 40; text: \"Hello World\" }";
879         QQmlComponent textComponent(&engine);
880         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
881         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
882
883         QCOMPARE(textObject->font().pointSize(), 40);
884         QCOMPARE(textObject->font().bold(), false);
885         QCOMPARE(textObject->font().italic(), false);
886
887         delete textObject;
888     }
889
890     {
891         QString componentStr = "import QtQuick 2.0\nText { font.pixelSize: 40; text: \"Hello World\" }";
892         QQmlComponent textComponent(&engine);
893         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
894         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
895
896         QCOMPARE(textObject->font().pixelSize(), 40);
897         QCOMPARE(textObject->font().bold(), false);
898         QCOMPARE(textObject->font().italic(), false);
899
900         delete textObject;
901     }
902
903     {
904         QString componentStr = "import QtQuick 2.0\nText { font.bold: true; text: \"Hello World\" }";
905         QQmlComponent textComponent(&engine);
906         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
907         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
908
909         QCOMPARE(textObject->font().bold(), true);
910         QCOMPARE(textObject->font().italic(), false);
911
912         delete textObject;
913     }
914
915     {
916         QString componentStr = "import QtQuick 2.0\nText { font.italic: true; text: \"Hello World\" }";
917         QQmlComponent textComponent(&engine);
918         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
919         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
920
921         QCOMPARE(textObject->font().italic(), true);
922         QCOMPARE(textObject->font().bold(), false);
923
924         delete textObject;
925     }
926
927     {
928         QString componentStr = "import QtQuick 2.0\nText { font.family: \"Helvetica\"; text: \"Hello World\" }";
929         QQmlComponent textComponent(&engine);
930         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
931         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
932
933         QCOMPARE(textObject->font().family(), QString("Helvetica"));
934         QCOMPARE(textObject->font().bold(), false);
935         QCOMPARE(textObject->font().italic(), false);
936
937         delete textObject;
938     }
939
940     {
941         QString componentStr = "import QtQuick 2.0\nText { font.family: \"\"; text: \"Hello World\" }";
942         QQmlComponent textComponent(&engine);
943         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
944         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
945
946         QCOMPARE(textObject->font().family(), QString(""));
947
948         delete textObject;
949     }
950 }
951
952 void tst_qquicktext::style()
953 {
954     //test style
955     for (int i = 0; i < styles.size(); i++)
956     {
957         QString componentStr = "import QtQuick 2.0\nText { style: \"" + styleStrings.at(i) + "\"; styleColor: \"white\"; text: \"Hello World\" }";
958         QQmlComponent textComponent(&engine);
959         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
960         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
961
962         QCOMPARE((int)textObject->style(), (int)styles.at(i));
963         QCOMPARE(textObject->styleColor(), QColor("white"));
964
965         delete textObject;
966     }
967     QString componentStr = "import QtQuick 2.0\nText { text: \"Hello World\" }";
968     QQmlComponent textComponent(&engine);
969     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
970     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
971
972     QRectF brPre = textObject->boundingRect();
973     textObject->setStyle(QQuickText::Outline);
974     QRectF brPost = textObject->boundingRect();
975
976     QVERIFY(brPre.width() < brPost.width());
977     QVERIFY(brPre.height() < brPost.height());
978
979     delete textObject;
980 }
981
982 void tst_qquicktext::color()
983 {
984     //test style
985     for (int i = 0; i < colorStrings.size(); i++)
986     {
987         QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
988         QQmlComponent textComponent(&engine);
989         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
990         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
991
992         QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
993         QCOMPARE(textObject->styleColor(), QColor("black"));
994         QCOMPARE(textObject->linkColor(), QColor("blue"));
995
996         delete textObject;
997     }
998
999     for (int i = 0; i < colorStrings.size(); i++)
1000     {
1001         QString componentStr = "import QtQuick 2.0\nText { styleColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1002         QQmlComponent textComponent(&engine);
1003         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1004         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1005
1006         QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(i)));
1007         // default color to black?
1008         QCOMPARE(textObject->color(), QColor("black"));
1009         QCOMPARE(textObject->linkColor(), QColor("blue"));
1010
1011         delete textObject;
1012     }
1013
1014     for (int i = 0; i < colorStrings.size(); i++)
1015     {
1016         QString componentStr = "import QtQuick 2.0\nText { linkColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1017         QQmlComponent textComponent(&engine);
1018         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1019         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1020
1021         QCOMPARE(textObject->styleColor(), QColor("black"));
1022         QCOMPARE(textObject->color(), QColor("black"));
1023         QCOMPARE(textObject->linkColor(), QColor(colorStrings.at(i)));
1024
1025         delete textObject;
1026     }
1027
1028     for (int i = 0; i < colorStrings.size(); i++)
1029     {
1030         for (int j = 0; j < colorStrings.size(); j++)
1031         {
1032             QString componentStr = "import QtQuick 2.0\nText { "
1033                     "color: \"" + colorStrings.at(i) + "\"; "
1034                     "styleColor: \"" + colorStrings.at(j) + "\"; "
1035                     "linkColor: \"" + colorStrings.at(j) + "\"; "
1036                     "text: \"Hello World\" }";
1037             QQmlComponent textComponent(&engine);
1038             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1039             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1040
1041             QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
1042             QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(j)));
1043             QCOMPARE(textObject->linkColor(), QColor(colorStrings.at(j)));
1044
1045             delete textObject;
1046         }
1047     }
1048     {
1049         QString colorStr = "#AA001234";
1050         QColor testColor("#001234");
1051         testColor.setAlpha(170);
1052
1053         QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStr + "\"; text: \"Hello World\" }";
1054         QQmlComponent textComponent(&engine);
1055         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1056         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1057
1058         QCOMPARE(textObject->color(), testColor);
1059
1060         delete textObject;
1061     } {
1062         QString colorStr = "#001234";
1063         QColor testColor(colorStr);
1064
1065         QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStr + "\"; text: \"Hello World\" }";
1066         QQmlComponent textComponent(&engine);
1067         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1068         QScopedPointer<QObject> object(textComponent.create());
1069         QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
1070
1071         QSignalSpy spy(textObject, SIGNAL(colorChanged()));
1072
1073         QCOMPARE(textObject->color(), testColor);
1074         textObject->setColor(testColor);
1075         QCOMPARE(textObject->color(), testColor);
1076         QCOMPARE(spy.count(), 0);
1077
1078         testColor = QColor("black");
1079         textObject->setColor(testColor);
1080         QCOMPARE(textObject->color(), testColor);
1081         QCOMPARE(spy.count(), 1);
1082     } {
1083         QString colorStr = "#001234";
1084         QColor testColor(colorStr);
1085
1086         QString componentStr = "import QtQuick 2.0\nText { styleColor: \"" + colorStr + "\"; text: \"Hello World\" }";
1087         QQmlComponent textComponent(&engine);
1088         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1089         QScopedPointer<QObject> object(textComponent.create());
1090         QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
1091
1092         QSignalSpy spy(textObject, SIGNAL(styleColorChanged()));
1093
1094         QCOMPARE(textObject->styleColor(), testColor);
1095         textObject->setStyleColor(testColor);
1096         QCOMPARE(textObject->styleColor(), testColor);
1097         QCOMPARE(spy.count(), 0);
1098
1099         testColor = QColor("black");
1100         textObject->setStyleColor(testColor);
1101         QCOMPARE(textObject->styleColor(), testColor);
1102         QCOMPARE(spy.count(), 1);
1103     } {
1104         QString colorStr = "#001234";
1105         QColor testColor(colorStr);
1106
1107         QString componentStr = "import QtQuick 2.0\nText { linkColor: \"" + colorStr + "\"; text: \"Hello World\" }";
1108         QQmlComponent textComponent(&engine);
1109         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1110         QScopedPointer<QObject> object(textComponent.create());
1111         QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
1112
1113         QSignalSpy spy(textObject, SIGNAL(linkColorChanged()));
1114
1115         QCOMPARE(textObject->linkColor(), testColor);
1116         textObject->setLinkColor(testColor);
1117         QCOMPARE(textObject->linkColor(), testColor);
1118         QCOMPARE(spy.count(), 0);
1119
1120         testColor = QColor("black");
1121         textObject->setLinkColor(testColor);
1122         QCOMPARE(textObject->linkColor(), testColor);
1123         QCOMPARE(spy.count(), 1);
1124     }
1125 }
1126
1127 void tst_qquicktext::smooth()
1128 {
1129     for (int i = 0; i < standard.size(); i++)
1130     {
1131         {
1132             QString componentStr = "import QtQuick 2.0\nText { smooth: false; text: \"" + standard.at(i) + "\" }";
1133             QQmlComponent textComponent(&engine);
1134             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1135             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1136             QCOMPARE(textObject->smooth(), false);
1137
1138             delete textObject;
1139         }
1140         {
1141             QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
1142             QQmlComponent textComponent(&engine);
1143             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1144             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1145             QCOMPARE(textObject->smooth(), true);
1146
1147             delete textObject;
1148         }
1149     }
1150     for (int i = 0; i < richText.size(); i++)
1151     {
1152         {
1153             QString componentStr = "import QtQuick 2.0\nText { smooth: false; text: \"" + richText.at(i) + "\" }";
1154             QQmlComponent textComponent(&engine);
1155             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1156             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1157             QCOMPARE(textObject->smooth(), false);
1158
1159             delete textObject;
1160         }
1161         {
1162             QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
1163             QQmlComponent textComponent(&engine);
1164             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1165             QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1166             QCOMPARE(textObject->smooth(), true);
1167
1168             delete textObject;
1169         }
1170     }
1171 }
1172
1173 void tst_qquicktext::weight()
1174 {
1175     {
1176         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1177         QQmlComponent textComponent(&engine);
1178         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1179         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1180
1181         QVERIFY(textObject != 0);
1182         QCOMPARE((int)textObject->font().weight(), (int)QQuickFontValueType::Normal);
1183
1184         delete textObject;
1185     }
1186     {
1187         QString componentStr = "import QtQuick 2.0\nText { font.weight: \"Bold\"; text: \"Hello world!\" }";
1188         QQmlComponent textComponent(&engine);
1189         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1190         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1191
1192         QVERIFY(textObject != 0);
1193         QCOMPARE((int)textObject->font().weight(), (int)QQuickFontValueType::Bold);
1194
1195         delete textObject;
1196     }
1197 }
1198
1199 void tst_qquicktext::underline()
1200 {
1201     {
1202         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1203         QQmlComponent textComponent(&engine);
1204         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1205         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1206
1207         QVERIFY(textObject != 0);
1208         QCOMPARE(textObject->font().underline(), false);
1209
1210         delete textObject;
1211     }
1212     {
1213         QString componentStr = "import QtQuick 2.0\nText { font.underline: true; text: \"Hello world!\" }";
1214         QQmlComponent textComponent(&engine);
1215         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1216         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1217
1218         QVERIFY(textObject != 0);
1219         QCOMPARE(textObject->font().underline(), true);
1220
1221         delete textObject;
1222     }
1223 }
1224
1225 void tst_qquicktext::overline()
1226 {
1227     {
1228         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1229         QQmlComponent textComponent(&engine);
1230         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1231         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1232
1233         QVERIFY(textObject != 0);
1234         QCOMPARE(textObject->font().overline(), false);
1235
1236         delete textObject;
1237     }
1238     {
1239         QString componentStr = "import QtQuick 2.0\nText { font.overline: true; text: \"Hello world!\" }";
1240         QQmlComponent textComponent(&engine);
1241         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1242         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1243
1244         QVERIFY(textObject != 0);
1245         QCOMPARE(textObject->font().overline(), true);
1246
1247         delete textObject;
1248     }
1249 }
1250
1251 void tst_qquicktext::strikeout()
1252 {
1253     {
1254         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1255         QQmlComponent textComponent(&engine);
1256         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1257         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1258
1259         QVERIFY(textObject != 0);
1260         QCOMPARE(textObject->font().strikeOut(), false);
1261
1262         delete textObject;
1263     }
1264     {
1265         QString componentStr = "import QtQuick 2.0\nText { font.strikeout: true; text: \"Hello world!\" }";
1266         QQmlComponent textComponent(&engine);
1267         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1268         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1269
1270         QVERIFY(textObject != 0);
1271         QCOMPARE(textObject->font().strikeOut(), true);
1272
1273         delete textObject;
1274     }
1275 }
1276
1277 void tst_qquicktext::capitalization()
1278 {
1279     {
1280         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1281         QQmlComponent textComponent(&engine);
1282         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1283         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1284
1285         QVERIFY(textObject != 0);
1286         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::MixedCase);
1287
1288         delete textObject;
1289     }
1290     {
1291         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllUppercase\" }";
1292         QQmlComponent textComponent(&engine);
1293         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1294         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1295
1296         QVERIFY(textObject != 0);
1297         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::AllUppercase);
1298
1299         delete textObject;
1300     }
1301     {
1302         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllLowercase\" }";
1303         QQmlComponent textComponent(&engine);
1304         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1305         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1306
1307         QVERIFY(textObject != 0);
1308         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::AllLowercase);
1309
1310         delete textObject;
1311     }
1312     {
1313         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"SmallCaps\" }";
1314         QQmlComponent textComponent(&engine);
1315         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1316         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1317
1318         QVERIFY(textObject != 0);
1319         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::SmallCaps);
1320
1321         delete textObject;
1322     }
1323     {
1324         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"Capitalize\" }";
1325         QQmlComponent textComponent(&engine);
1326         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1327         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1328
1329         QVERIFY(textObject != 0);
1330         QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::Capitalize);
1331
1332         delete textObject;
1333     }
1334 }
1335
1336 void tst_qquicktext::letterSpacing()
1337 {
1338     {
1339         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1340         QQmlComponent textComponent(&engine);
1341         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1342         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1343
1344         QVERIFY(textObject != 0);
1345         QCOMPARE(textObject->font().letterSpacing(), 0.0);
1346
1347         delete textObject;
1348     }
1349     {
1350         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: -2 }";
1351         QQmlComponent textComponent(&engine);
1352         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1353         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1354
1355         QVERIFY(textObject != 0);
1356         QCOMPARE(textObject->font().letterSpacing(), -2.);
1357
1358         delete textObject;
1359     }
1360     {
1361         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: 3 }";
1362         QQmlComponent textComponent(&engine);
1363         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1364         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1365
1366         QVERIFY(textObject != 0);
1367         QCOMPARE(textObject->font().letterSpacing(), 3.);
1368
1369         delete textObject;
1370     }
1371 }
1372
1373 void tst_qquicktext::wordSpacing()
1374 {
1375     {
1376         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1377         QQmlComponent textComponent(&engine);
1378         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1379         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1380
1381         QVERIFY(textObject != 0);
1382         QCOMPARE(textObject->font().wordSpacing(), 0.0);
1383
1384         delete textObject;
1385     }
1386     {
1387         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: -50 }";
1388         QQmlComponent textComponent(&engine);
1389         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1390         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1391
1392         QVERIFY(textObject != 0);
1393         QCOMPARE(textObject->font().wordSpacing(), -50.);
1394
1395         delete textObject;
1396     }
1397     {
1398         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: 200 }";
1399         QQmlComponent textComponent(&engine);
1400         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1401         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1402
1403         QVERIFY(textObject != 0);
1404         QCOMPARE(textObject->font().wordSpacing(), 200.);
1405
1406         delete textObject;
1407     }
1408 }
1409
1410
1411
1412
1413 class EventSender : public QQuickItem
1414 {
1415 public:
1416     void sendEvent(QMouseEvent *event) {
1417         if (event->type() == QEvent::MouseButtonPress)
1418             mousePressEvent(event);
1419         else if (event->type() == QEvent::MouseButtonRelease)
1420             mouseReleaseEvent(event);
1421         else
1422             qWarning() << "Trying to send unsupported event type";
1423     }
1424 };
1425
1426 class LinkTest : public QObject
1427 {
1428     Q_OBJECT
1429 public:
1430     LinkTest() {}
1431
1432     QString link;
1433
1434 public slots:
1435     void linkClicked(QString l) { link = l; }
1436 };
1437
1438 void tst_qquicktext::clickLink()
1439 {
1440     {
1441         QString componentStr = "import QtQuick 2.0\nText { text: \"<a href=\\\"http://qt.nokia.com\\\">Hello world!</a>\" }";
1442         QQmlComponent textComponent(&engine);
1443         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1444         QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1445
1446         QVERIFY(textObject != 0);
1447
1448         LinkTest test;
1449         QObject::connect(textObject, SIGNAL(linkActivated(QString)), &test, SLOT(linkClicked(QString)));
1450
1451         {
1452             QMouseEvent me(QEvent::MouseButtonPress,QPointF(textObject->x()/2, textObject->y()/2), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1453             static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
1454
1455         }
1456
1457         {
1458             QMouseEvent me(QEvent::MouseButtonRelease,QPointF(textObject->x()/2, textObject->y()/2), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1459             static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
1460
1461         }
1462
1463
1464         QCOMPARE(test.link, QLatin1String("http://qt.nokia.com"));
1465
1466         delete textObject;
1467     }
1468 }
1469
1470 void tst_qquicktext::baseUrl()
1471 {
1472     QUrl localUrl("file:///tests/text.qml");
1473     QUrl remoteUrl("http://qt.nokia.com/test.qml");
1474
1475     QQmlComponent textComponent(&engine);
1476     textComponent.setData("import QtQuick 2.0\n Text {}", localUrl);
1477     QQuickText *textObject = qobject_cast<QQuickText *>(textComponent.create());
1478
1479     QCOMPARE(textObject->baseUrl(), localUrl);
1480
1481     QSignalSpy spy(textObject, SIGNAL(baseUrlChanged()));
1482
1483     textObject->setBaseUrl(localUrl);
1484     QCOMPARE(textObject->baseUrl(), localUrl);
1485     QCOMPARE(spy.count(), 0);
1486
1487     textObject->setBaseUrl(remoteUrl);
1488     QCOMPARE(textObject->baseUrl(), remoteUrl);
1489     QCOMPARE(spy.count(), 1);
1490
1491     textObject->resetBaseUrl();
1492     QCOMPARE(textObject->baseUrl(), localUrl);
1493     QCOMPARE(spy.count(), 2);
1494 }
1495
1496 void tst_qquicktext::embeddedImages_data()
1497 {
1498     QTest::addColumn<QUrl>("qmlfile");
1499     QTest::addColumn<QString>("error");
1500     QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
1501     QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
1502         << testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML Text: Cannot open: " + testFileUrl("http/notexists.png").toString();
1503     QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
1504     QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
1505     QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
1506         << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error downloading http://127.0.0.1:14453/notexists.png - server replied: Not found";
1507     QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
1508 }
1509
1510 void tst_qquicktext::embeddedImages()
1511 {
1512     // Tests QTBUG-9900
1513
1514     QFETCH(QUrl, qmlfile);
1515     QFETCH(QString, error);
1516
1517     TestHTTPServer server(14453);
1518     server.serveDirectory(testFile("http"));
1519
1520     if (!error.isEmpty())
1521         QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
1522
1523     QQmlComponent textComponent(&engine, qmlfile);
1524     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1525
1526     QVERIFY(textObject != 0);
1527
1528     QTRY_COMPARE(textObject->resourcesLoading(), 0);
1529
1530     QPixmap pm(testFile("http/exists.png"));
1531     if (error.isEmpty()) {
1532         QCOMPARE(textObject->width(), double(pm.width()));
1533         QCOMPARE(textObject->height(), double(pm.height()));
1534     } else {
1535         QVERIFY(16 != pm.width()); // check test is effective
1536         QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
1537         QCOMPARE(textObject->height(), 16.0);
1538     }
1539
1540     delete textObject;
1541 }
1542
1543 void tst_qquicktext::lineCount()
1544 {
1545     QQuickView *canvas = createView(testFile("lineCount.qml"));
1546
1547     QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
1548     QVERIFY(myText != 0);
1549
1550     QVERIFY(myText->lineCount() > 1);
1551     QVERIFY(!myText->truncated());
1552     QCOMPARE(myText->maximumLineCount(), INT_MAX);
1553
1554     myText->setMaximumLineCount(2);
1555     QCOMPARE(myText->lineCount(), 2);
1556     QCOMPARE(myText->truncated(), true);
1557     QCOMPARE(myText->maximumLineCount(), 2);
1558
1559     myText->resetMaximumLineCount();
1560     QCOMPARE(myText->maximumLineCount(), INT_MAX);
1561     QCOMPARE(myText->truncated(), false);
1562
1563     myText->setElideMode(QQuickText::ElideRight);
1564     myText->setMaximumLineCount(2);
1565     QCOMPARE(myText->lineCount(), 2);
1566     QCOMPARE(myText->truncated(), true);
1567     QCOMPARE(myText->maximumLineCount(), 2);
1568
1569     delete canvas;
1570 }
1571
1572 void tst_qquicktext::lineHeight()
1573 {
1574     QQuickView *canvas = createView(testFile("lineHeight.qml"));
1575
1576     QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
1577     QVERIFY(myText != 0);
1578
1579     QVERIFY(myText->lineHeight() == 1);
1580     QVERIFY(myText->lineHeightMode() == QQuickText::ProportionalHeight);
1581
1582     qreal h = myText->height();
1583     myText->setLineHeight(1.5);
1584     QCOMPARE(myText->height(), qreal(qCeil(h * 1.5)));
1585
1586     myText->setLineHeightMode(QQuickText::FixedHeight);
1587     myText->setLineHeight(20);
1588     QCOMPARE(myText->height(), myText->lineCount() * 20.0);
1589
1590     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.");
1591     myText->setLineHeightMode(QQuickText::ProportionalHeight);
1592     myText->setLineHeight(1.0);
1593
1594     qreal h2 = myText->height();
1595     myText->setLineHeight(2.0);
1596     QVERIFY(myText->height() == h2 * 2.0);
1597
1598     myText->setLineHeightMode(QQuickText::FixedHeight);
1599     myText->setLineHeight(10);
1600     QCOMPARE(myText->height(), myText->lineCount() * 10.0);
1601
1602     delete canvas;
1603 }
1604
1605 void tst_qquicktext::implicitSize_data()
1606 {
1607     QTest::addColumn<QString>("text");
1608     QTest::addColumn<QString>("width");
1609     QTest::addColumn<QString>("wrap");
1610     QTest::addColumn<QString>("elide");
1611     QTest::addColumn<QString>("format");
1612     QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.NoWrap" << "Text.ElideNone" << "Text.PlainText";
1613     QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 50" << "Text.NoWrap" << "Text.ElideNone" << "Text.RichText";
1614     QTest::newRow("styledtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 50" << "Text.NoWrap" << "Text.ElideNone" << "Text.StyledText";
1615     QTest::newRow("plain, 0 width") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.NoWrap" << "Text.ElideNone" << "Text.PlainText";
1616     QTest::newRow("plain, elide") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.NoWrap" << "Text.ElideRight" << "Text.PlainText";
1617     QTest::newRow("plain, 0 width, elide") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.NoWrap" << "Text.ElideRight" << "Text.PlainText";
1618     QTest::newRow("richtext, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 0" << "Text.NoWrap" << "Text.ElideNone" << "Text.RichText";
1619     QTest::newRow("styledtext, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 0" << "Text.NoWrap" << "Text.ElideNone" << "Text.StyledText";
1620     QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.Wrap" << "Text.ElideNone" << "Text.PlainText";
1621     QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "50" << "Text.Wrap" << "Text.ElideNone" << "Text.RichText";
1622     QTest::newRow("styledtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "50" << "Text.Wrap" << "Text.ElideNone" << "Text.StyledText";
1623     QTest::newRow("plain_wrap, 0 width") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.Wrap" << "Text.ElideNone" << "Text.PlainText";
1624     QTest::newRow("plain_wrap, elide") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.Wrap" << "Text.ElideRight" << "Text.PlainText";
1625     QTest::newRow("plain_wrap, 0 width, elide") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.Wrap" << "Text.ElideRight" << "Text.PlainText";
1626     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";
1627     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";
1628 }
1629
1630 void tst_qquicktext::implicitSize()
1631 {
1632     QFETCH(QString, text);
1633     QFETCH(QString, width);
1634     QFETCH(QString, format);
1635     QFETCH(QString, wrap);
1636     QFETCH(QString, elide);
1637     QString componentStr = "import QtQuick 2.0\nText { "
1638             "property real iWidth: implicitWidth; "
1639             "text: \"" + text + "\"; "
1640             "width: " + width + "; "
1641             "textFormat: " + format + "; "
1642             "wrapMode: " + wrap + "; "
1643             "elide: " + elide + "; "
1644             "maximumLineCount: 2 }";
1645     QQmlComponent textComponent(&engine);
1646     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1647     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1648
1649     QVERIFY(textObject->width() < textObject->implicitWidth());
1650     QVERIFY(textObject->height() == textObject->implicitHeight());
1651     QCOMPARE(textObject->property("iWidth").toReal(), textObject->implicitWidth());
1652
1653     textObject->resetWidth();
1654     QVERIFY(textObject->width() == textObject->implicitWidth());
1655     QVERIFY(textObject->height() == textObject->implicitHeight());
1656
1657     delete textObject;
1658 }
1659
1660 void tst_qquicktext::contentSize()
1661 {
1662     QString componentStr = "import QtQuick 2.0\nText { width: 75; height: 16; font.pixelSize: 10 }";
1663     QQmlComponent textComponent(&engine);
1664     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1665     QScopedPointer<QObject> object(textComponent.create());
1666     QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
1667
1668     QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
1669
1670     textObject->setText("The quick red fox jumped over the lazy brown dog");
1671
1672     QVERIFY(textObject->contentWidth() > textObject->width());
1673     QVERIFY(textObject->contentHeight() < textObject->height());
1674     QCOMPARE(spy.count(), 1);
1675
1676     textObject->setWrapMode(QQuickText::WordWrap);
1677     QVERIFY(textObject->contentWidth() <= textObject->width());
1678     QVERIFY(textObject->contentHeight() > textObject->height());
1679     QCOMPARE(spy.count(), 2);
1680
1681     textObject->setElideMode(QQuickText::ElideRight);
1682     QVERIFY(textObject->contentWidth() <= textObject->width());
1683     QVERIFY(textObject->contentHeight() < textObject->height());
1684     QCOMPARE(spy.count(), 3);
1685     int spyCount = 3;
1686     qreal elidedWidth = textObject->contentWidth();
1687
1688     textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
1689     QVERIFY(textObject->contentWidth() <= textObject->width());
1690     QVERIFY(textObject->contentHeight() < textObject->height());
1691     // this text probably won't have the same elided width, but it's not guaranteed.
1692     if (textObject->contentWidth() != elidedWidth)
1693         QCOMPARE(spy.count(), ++spyCount);
1694     else
1695         QCOMPARE(spy.count(), spyCount);
1696
1697     textObject->setElideMode(QQuickText::ElideNone);
1698     QVERIFY(textObject->contentWidth() > textObject->width());
1699     QVERIFY(textObject->contentHeight() > textObject->height());
1700     QCOMPARE(spy.count(), ++spyCount);
1701 }
1702
1703 void tst_qquicktext::implicitSizeBinding_data()
1704 {
1705     implicitSize_data();
1706 }
1707
1708 void tst_qquicktext::implicitSizeBinding()
1709 {
1710     QFETCH(QString, text);
1711     QFETCH(QString, wrap);
1712     QFETCH(QString, format);
1713     QString componentStr = "import QtQuick 2.0\nText { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + "; textFormat: " + format + " }";
1714
1715     QQmlComponent textComponent(&engine);
1716     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1717     QScopedPointer<QObject> object(textComponent.create());
1718     QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
1719
1720     QCOMPARE(textObject->width(), textObject->implicitWidth());
1721     QCOMPARE(textObject->height(), textObject->implicitHeight());
1722
1723     textObject->resetWidth();
1724     QCOMPARE(textObject->width(), textObject->implicitWidth());
1725     QCOMPARE(textObject->height(), textObject->implicitHeight());
1726
1727     textObject->resetHeight();
1728     QCOMPARE(textObject->width(), textObject->implicitWidth());
1729     QCOMPARE(textObject->height(), textObject->implicitHeight());
1730 }
1731
1732 void tst_qquicktext::boundingRect_data()
1733 {
1734     QTest::addColumn<QString>("format");
1735     QTest::newRow("PlainText") << "Text.PlainText";
1736     QTest::newRow("StyledText") << "Text.StyledText";
1737     QTest::newRow("RichText") << "Text.RichText";
1738 }
1739
1740 void tst_qquicktext::boundingRect()
1741 {
1742     QFETCH(QString, format);
1743
1744     QQmlComponent component(&engine);
1745     component.setData("import QtQuick 2.0\n Text { textFormat:" + format.toUtf8() + "}", QUrl());
1746     QScopedPointer<QObject> object(component.create());
1747     QQuickText *text = qobject_cast<QQuickText *>(object.data());
1748     QVERIFY(text);
1749
1750     QCOMPARE(text->boundingRect().x(), qreal(0));
1751     QCOMPARE(text->boundingRect().y(), qreal(0));
1752     QCOMPARE(text->boundingRect().width(), qreal(0));
1753     QCOMPARE(text->boundingRect().height(), qreal(qCeil(QFontMetricsF(text->font()).height())));
1754
1755     text->setText("Hello World");
1756
1757     QTextLayout layout(text->text());
1758     layout.setFont(text->font());
1759
1760     if (!qmlDisableDistanceField()) {
1761         QTextOption option;
1762         option.setUseDesignMetrics(true);
1763         layout.setTextOption(option);
1764     }
1765     layout.beginLayout();
1766     QTextLine line = layout.createLine();
1767     layout.endLayout();
1768
1769     QCOMPARE(text->boundingRect().x(), qreal(0));
1770     QCOMPARE(text->boundingRect().y(), qreal(0));
1771     QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
1772     QCOMPARE(text->boundingRect().height(), line.height());
1773
1774     // the size of the bounding rect shouldn't be bounded by the size of item.
1775     text->setWidth(text->width() / 2);
1776     QCOMPARE(text->boundingRect().x(), qreal(0));
1777     QCOMPARE(text->boundingRect().y(), qreal(0));
1778     QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
1779     QCOMPARE(text->boundingRect().height(), line.height());
1780
1781     text->setHeight(text->height() * 2);
1782     QCOMPARE(text->boundingRect().x(), qreal(0));
1783     QCOMPARE(text->boundingRect().y(), qreal(0));
1784     QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
1785     QCOMPARE(text->boundingRect().height(), line.height());
1786
1787     text->setHAlign(QQuickText::AlignRight);
1788     QCOMPARE(text->boundingRect().x(), text->width() - line.naturalTextWidth());
1789     QCOMPARE(text->boundingRect().y(), qreal(0));
1790     QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
1791     QCOMPARE(text->boundingRect().height(), line.height());
1792
1793     text->setWrapMode(QQuickText::Wrap);
1794     QCOMPARE(text->boundingRect().right(), text->width());
1795     QCOMPARE(text->boundingRect().y(), qreal(0));
1796     QVERIFY(text->boundingRect().width() < line.naturalTextWidth());
1797     QVERIFY(text->boundingRect().height() > line.height());
1798
1799     text->setVAlign(QQuickText::AlignBottom);
1800     QCOMPARE(text->boundingRect().right(), text->width());
1801     QCOMPARE(text->boundingRect().bottom(), text->height());
1802     QVERIFY(text->boundingRect().width() < line.naturalTextWidth());
1803     QVERIFY(text->boundingRect().height() > line.height());
1804 }
1805
1806 void tst_qquicktext::clipRect()
1807 {
1808     QQmlComponent component(&engine);
1809     component.setData("import QtQuick 2.0\n Text {}", QUrl());
1810     QScopedPointer<QObject> object(component.create());
1811     QQuickText *text = qobject_cast<QQuickText *>(object.data());
1812     QVERIFY(text);
1813
1814     QTextLayout layout;
1815     layout.setFont(text->font());
1816
1817     QCOMPARE(text->clipRect().x(), qreal(0));
1818     QCOMPARE(text->clipRect().y(), qreal(0));
1819     QCOMPARE(text->clipRect().width(), text->width());
1820     QCOMPARE(text->clipRect().height(), text->height());
1821
1822     text->setText("Hello World");
1823
1824     QCOMPARE(text->clipRect().x(), qreal(0));
1825     QCOMPARE(text->clipRect().y(), qreal(0));
1826     QCOMPARE(text->clipRect().width(), text->width());
1827     QCOMPARE(text->clipRect().height(), text->height());
1828
1829     // Clip rect follows the item not content dimensions.
1830     text->setWidth(text->width() / 2);
1831     QCOMPARE(text->clipRect().x(), qreal(0));
1832     QCOMPARE(text->clipRect().y(), qreal(0));
1833     QCOMPARE(text->clipRect().width(), text->width());
1834     QCOMPARE(text->clipRect().height(), text->height());
1835
1836     text->setHeight(text->height() * 2);
1837     QCOMPARE(text->clipRect().x(), qreal(0));
1838     QCOMPARE(text->clipRect().y(), qreal(0));
1839     QCOMPARE(text->clipRect().width(), text->width());
1840     QCOMPARE(text->clipRect().height(), text->height());
1841
1842     // Setting a style adds a small amount of padding to the clip rect.
1843     text->setStyle(QQuickText::Outline);
1844     QCOMPARE(text->clipRect().x(), qreal(-1));
1845     QCOMPARE(text->clipRect().y(), qreal(0));
1846     QCOMPARE(text->clipRect().width(), text->width() + 2);
1847     QCOMPARE(text->clipRect().height(), text->height() + 2);
1848 }
1849
1850 void tst_qquicktext::lineLaidOut()
1851 {
1852     QQuickView *canvas = createView(testFile("lineLayout.qml"));
1853
1854     QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
1855     QVERIFY(myText != 0);
1856
1857     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
1858     QVERIFY(textPrivate != 0);
1859
1860     QVERIFY(!textPrivate->extra.isAllocated());
1861
1862 #if defined(Q_OS_MAC)
1863     QVERIFY(myText->lineCount() == textPrivate->linesRects.count());
1864 #endif
1865
1866     for (int i = 0; i < textPrivate->layout.lineCount(); ++i) {
1867         QRectF r = textPrivate->layout.lineAt(i).rect();
1868         QVERIFY(r.width() == i * 15);
1869         if (i >= 30)
1870             QVERIFY(r.x() == r.width() + 30);
1871         if (i >= 60) {
1872             QVERIFY(r.x() == r.width() * 2 + 60);
1873             QVERIFY(r.height() == 20);
1874         }
1875     }
1876
1877     delete canvas;
1878 }
1879
1880 void tst_qquicktext::imgTagsBaseUrl_data()
1881 {
1882     QTest::addColumn<QUrl>("src");
1883     QTest::addColumn<QUrl>("baseUrl");
1884     QTest::addColumn<QUrl>("contextUrl");
1885     QTest::addColumn<qreal>("imgHeight");
1886
1887     QTest::newRow("absolute local")
1888             << testFileUrl("images/heart200.png")
1889             << QUrl()
1890             << QUrl()
1891             << 181.;
1892     QTest::newRow("relative local context 1")
1893             << QUrl("images/heart200.png")
1894             << QUrl()
1895             << testFileUrl("/app.qml")
1896             << 181.;
1897     QTest::newRow("relative local context 2")
1898             << QUrl("heart200.png")
1899             << QUrl()
1900             << testFileUrl("images/app.qml")
1901             << 181.;
1902     QTest::newRow("relative local base 1")
1903             << QUrl("images/heart200.png")
1904             << testFileUrl("")
1905             << testFileUrl("nonexistant/app.qml")
1906             << 181.;
1907     QTest::newRow("relative local base 2")
1908             << QUrl("heart200.png")
1909             << testFileUrl("images/")
1910             << testFileUrl("nonexistant/app.qml")
1911             << 181.;
1912     QTest::newRow("base relative to local context")
1913             << QUrl("heart200.png")
1914             << testFileUrl("images/")
1915             << testFileUrl("/app.qml")
1916             << 181.;
1917
1918     QTest::newRow("absolute remote")
1919             << QUrl("http://127.0.0.1:14453/images/heart200.png")
1920             << QUrl()
1921             << QUrl()
1922             << 181.;
1923     QTest::newRow("relative remote base 1")
1924             << QUrl("images/heart200.png")
1925             << QUrl("http://127.0.0.1:14453/")
1926             << testFileUrl("nonexistant/app.qml")
1927             << 181.;
1928     QTest::newRow("relative remote base 2")
1929             << QUrl("heart200.png")
1930             << QUrl("http://127.0.0.1:14453/images/")
1931             << testFileUrl("nonexistant/app.qml")
1932             << 181.;
1933 }
1934
1935 void tst_qquicktext::imgTagsBaseUrl()
1936 {
1937     QFETCH(QUrl, src);
1938     QFETCH(QUrl, baseUrl);
1939     QFETCH(QUrl, contextUrl);
1940     QFETCH(qreal, imgHeight);
1941
1942     TestHTTPServer server(14453);
1943     server.serveDirectory(testFile(""));
1944
1945     QByteArray baseUrlFragment;
1946     if (!baseUrl.isEmpty())
1947         baseUrlFragment = "; baseUrl: \"" + baseUrl.toEncoded() + "\"";
1948     QByteArray componentStr = "import QtQuick 2.0\nText { text: \"This is a test <img src=\\\"" + src.toEncoded() + "\\\">\"" + baseUrlFragment + " }";
1949
1950     QQmlComponent component(&engine);
1951     component.setData(componentStr, contextUrl);
1952     QScopedPointer<QObject> object(component.create());
1953     QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
1954     QVERIFY(textObject);
1955
1956     QCoreApplication::processEvents();
1957
1958     QTRY_COMPARE(textObject->height(), imgHeight);
1959 }
1960
1961 void tst_qquicktext::imgTagsAlign_data()
1962 {
1963     QTest::addColumn<QString>("src");
1964     QTest::addColumn<int>("imgHeight");
1965     QTest::addColumn<QString>("align");
1966     QTest::newRow("heart-bottom") << "data/images/heart200.png" << 181 <<  "bottom";
1967     QTest::newRow("heart-middle") << "data/images/heart200.png" << 181 <<  "middle";
1968     QTest::newRow("heart-top") << "data/images/heart200.png" << 181 <<  "top";
1969     QTest::newRow("starfish-bottom") << "data/images/starfish_2.png" << 217 <<  "bottom";
1970     QTest::newRow("starfish-middle") << "data/images/starfish_2.png" << 217 <<  "middle";
1971     QTest::newRow("starfish-top") << "data/images/starfish_2.png" << 217 <<  "top";
1972 }
1973
1974 void tst_qquicktext::imgTagsAlign()
1975 {
1976     QFETCH(QString, src);
1977     QFETCH(int, imgHeight);
1978     QFETCH(QString, align);
1979     QString componentStr = "import QtQuick 2.0\nText { text: \"This is a test <img src=\\\"" + src + "\\\" align=\\\"" + align + "\\\"> of image.\" }";
1980     QQmlComponent textComponent(&engine);
1981     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1982     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
1983
1984     QVERIFY(textObject != 0);
1985     QVERIFY(textObject->height() == imgHeight);
1986
1987     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
1988     QVERIFY(textPrivate != 0);
1989
1990     QRectF br = textPrivate->layout.boundingRect();
1991     if (align == "bottom")
1992         QVERIFY(br.y() == imgHeight - br.height());
1993     else if (align == "middle")
1994         QVERIFY(br.y() == imgHeight / 2.0 - br.height() / 2.0);
1995     else if (align == "top")
1996         QVERIFY(br.y() == 0);
1997
1998     delete textObject;
1999 }
2000
2001 void tst_qquicktext::imgTagsMultipleImages()
2002 {
2003     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\\\">.\" }";
2004
2005     QQmlComponent textComponent(&engine);
2006     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2007     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
2008
2009     QVERIFY(textObject != 0);
2010     QVERIFY(textObject->height() == 85);
2011
2012     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
2013     QVERIFY(textPrivate != 0);
2014     QVERIFY(textPrivate->visibleImgTags.count() == 2);
2015
2016     delete textObject;
2017 }
2018
2019 void tst_qquicktext::imgTagsElide()
2020 {
2021     QQuickView *canvas = createView(testFile("imgTagsElide.qml"));
2022     QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
2023     QVERIFY(myText != 0);
2024
2025     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
2026     QVERIFY(textPrivate != 0);
2027     QVERIFY(textPrivate->visibleImgTags.count() == 0);
2028     myText->setMaximumLineCount(20);
2029     QTRY_VERIFY(textPrivate->visibleImgTags.count() == 1);
2030
2031     delete myText;
2032     delete canvas;
2033 }
2034
2035 void tst_qquicktext::imgTagsUpdates()
2036 {
2037     QQuickView *canvas = createView(testFile("imgTagsUpdates.qml"));
2038     QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
2039     QVERIFY(myText != 0);
2040
2041     QSignalSpy spy(myText, SIGNAL(contentSizeChanged()));
2042
2043     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
2044     QVERIFY(textPrivate != 0);
2045
2046     myText->setText("This is a heart<img src=\"images/heart200.png\">.");
2047     QVERIFY(textPrivate->visibleImgTags.count() == 1);
2048     QVERIFY(spy.count() == 1);
2049
2050     myText->setMaximumLineCount(2);
2051     myText->setText("This is another heart<img src=\"images/heart200.png\">.");
2052     QTRY_VERIFY(textPrivate->visibleImgTags.count() == 1);
2053
2054     // if maximumLineCount is set and the img tag doesn't have an explicit size
2055     // we relayout twice.
2056     QVERIFY(spy.count() == 3);
2057
2058     delete myText;
2059     delete canvas;
2060 }
2061
2062 void tst_qquicktext::imgTagsError()
2063 {
2064     QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"data/images/starfish_2.pn\\\" width=\\\"60\\\" height=\\\"60\\\">.\" }";
2065
2066     QQmlComponent textComponent(&engine);
2067     QTest::ignoreMessage(QtWarningMsg, "file::2:1: QML Text: Cannot open: file:data/images/starfish_2.pn");
2068     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2069     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
2070
2071     QVERIFY(textObject != 0);
2072     delete textObject;
2073 }
2074
2075 void tst_qquicktext::fontSizeMode_data()
2076 {
2077     QTest::addColumn<QString>("text");
2078     QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog";
2079     QTest::newRow("styled") << "<b>The quick red fox jumped over the lazy brown dog</b>";
2080 }
2081
2082 void tst_qquicktext::fontSizeMode()
2083 {
2084     QFETCH(QString, text);
2085
2086     QScopedPointer<QQuickView> canvas(createView(testFile("fontSizeMode.qml")));
2087     canvas->show();
2088
2089     QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
2090     QVERIFY(myText != 0);
2091
2092     myText->setText(text);
2093     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2094
2095     qreal originalWidth = myText->contentWidth();
2096     qreal originalHeight = myText->contentHeight();
2097
2098     // The original text unwrapped should exceed the width of the item.
2099     QVERIFY(originalWidth > myText->width());
2100     QVERIFY(originalHeight < myText->height());
2101
2102     QFont font = myText->font();
2103     font.setPixelSize(64);
2104
2105     myText->setFont(font);
2106     myText->setFontSizeMode(QQuickText::HorizontalFit);
2107     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2108     // Font size reduced to fit within the width of the item.
2109     qreal horizontalFitWidth = myText->contentWidth();
2110     qreal horizontalFitHeight = myText->contentHeight();
2111     QVERIFY(horizontalFitWidth <= myText->width() + 2); // rounding
2112     QVERIFY(horizontalFitHeight <= myText->height() + 2);
2113
2114     // Elide won't affect the size with HorizontalFit.
2115     myText->setElideMode(QQuickText::ElideRight);
2116     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2117     QVERIFY(!myText->truncated());
2118     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2119     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2120
2121     myText->setElideMode(QQuickText::ElideLeft);
2122     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2123     QVERIFY(!myText->truncated());
2124     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2125     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2126
2127     myText->setElideMode(QQuickText::ElideMiddle);
2128     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2129     QVERIFY(!myText->truncated());
2130     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2131     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2132
2133     myText->setElideMode(QQuickText::ElideNone);
2134     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2135
2136     myText->setFontSizeMode(QQuickText::VerticalFit);
2137     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2138     // Font size increased to fill the height of the item.
2139     qreal verticalFitHeight = myText->contentHeight();
2140     QVERIFY(myText->contentWidth() > myText->width());
2141     QVERIFY(verticalFitHeight <= myText->height() + 2);
2142     QVERIFY(verticalFitHeight > originalHeight);
2143
2144     // Elide won't affect the height of a single line with VerticalFit but will crop the width.
2145     myText->setElideMode(QQuickText::ElideRight);
2146     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2147     QVERIFY(myText->truncated());
2148     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2149     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2150
2151     myText->setElideMode(QQuickText::ElideLeft);
2152     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2153     QVERIFY(myText->truncated());
2154     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2155     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2156
2157     myText->setElideMode(QQuickText::ElideMiddle);
2158     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2159     QVERIFY(myText->truncated());
2160     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2161     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2162
2163     myText->setElideMode(QQuickText::ElideNone);
2164     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2165
2166     myText->setFontSizeMode(QQuickText::Fit);
2167     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2168     // Should be the same as HorizontalFit with no wrapping.
2169     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2170     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2171
2172     // Elide won't affect the size with Fit.
2173     myText->setElideMode(QQuickText::ElideRight);
2174     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2175     QVERIFY(!myText->truncated());
2176     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2177     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2178
2179     myText->setElideMode(QQuickText::ElideLeft);
2180     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2181     QVERIFY(!myText->truncated());
2182     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2183     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2184
2185     myText->setElideMode(QQuickText::ElideMiddle);
2186     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2187     QVERIFY(!myText->truncated());
2188     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2189     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2190
2191     myText->setElideMode(QQuickText::ElideNone);
2192     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2193
2194     myText->setFontSizeMode(QQuickText::FixedSize);
2195     myText->setWrapMode(QQuickText::Wrap);
2196     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2197
2198     originalWidth = myText->contentWidth();
2199     originalHeight = myText->contentHeight();
2200
2201     // The original text wrapped should exceed the height of the item.
2202     QVERIFY(originalWidth <= myText->width() + 2);
2203     QVERIFY(originalHeight > myText->height());
2204
2205     myText->setFontSizeMode(QQuickText::HorizontalFit);
2206     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2207     // HorizontalFit should reduce the font size to minimize wrapping, which brings it back to the
2208     // same size as without text wrapping.
2209     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2210     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2211
2212     // Elide won't affect the size with HorizontalFit.
2213     myText->setElideMode(QQuickText::ElideRight);
2214     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2215     QVERIFY(!myText->truncated());
2216     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2217     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2218
2219     myText->setElideMode(QQuickText::ElideNone);
2220     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2221
2222     myText->setFontSizeMode(QQuickText::VerticalFit);
2223     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2224     // VerticalFit should reduce the size to the wrapped text within the vertical height.
2225     verticalFitHeight = myText->contentHeight();
2226     qreal verticalFitWidth = myText->contentWidth();
2227     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2228     QVERIFY(verticalFitHeight <= myText->height() + 2);
2229     QVERIFY(verticalFitHeight < originalHeight);
2230
2231     // Elide won't affect the height or width of a wrapped text with VerticalFit.
2232     myText->setElideMode(QQuickText::ElideRight);
2233     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2234     QVERIFY(!myText->truncated());
2235     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2236     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2237
2238     myText->setElideMode(QQuickText::ElideNone);
2239     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2240
2241     myText->setFontSizeMode(QQuickText::Fit);
2242     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2243     // Should be the same as VerticalFit with wrapping.
2244     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2245     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2246
2247     // Elide won't affect the size with Fit.
2248     myText->setElideMode(QQuickText::ElideRight);
2249     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2250     QVERIFY(!myText->truncated());
2251     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2252     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2253
2254     myText->setElideMode(QQuickText::ElideNone);
2255     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2256
2257     myText->setFontSizeMode(QQuickText::FixedSize);
2258     myText->setMaximumLineCount(2);
2259     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2260
2261     // The original text wrapped should exceed the height of the item.
2262     QVERIFY(originalWidth <= myText->width() + 2);
2263     QVERIFY(originalHeight > myText->height());
2264
2265     myText->setFontSizeMode(QQuickText::HorizontalFit);
2266     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2267     // HorizontalFit should reduce the font size to minimize wrapping, which brings it back to the
2268     // same size as without text wrapping.
2269     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2270     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2271
2272     // Elide won't affect the size with HorizontalFit.
2273     myText->setElideMode(QQuickText::ElideRight);
2274     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2275     QVERIFY(!myText->truncated());
2276     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2277     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2278
2279     myText->setElideMode(QQuickText::ElideNone);
2280     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2281
2282     myText->setFontSizeMode(QQuickText::VerticalFit);
2283     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2284     // VerticalFit should reduce the size to the wrapped text within the vertical height.
2285     verticalFitHeight = myText->contentHeight();
2286     verticalFitWidth = myText->contentWidth();
2287     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2288     QVERIFY(verticalFitHeight <= myText->height() + 2);
2289     QVERIFY(verticalFitHeight < originalHeight);
2290
2291     // Elide won't affect the height or width of a wrapped text with VerticalFit.
2292     myText->setElideMode(QQuickText::ElideRight);
2293     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2294     QVERIFY(!myText->truncated());
2295     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2296     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2297
2298     myText->setElideMode(QQuickText::ElideNone);
2299     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2300
2301     myText->setFontSizeMode(QQuickText::Fit);
2302     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2303     // Should be the same as VerticalFit with wrapping.
2304     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2305     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2306
2307     // Elide won't affect the size with Fit.
2308     myText->setElideMode(QQuickText::ElideRight);
2309     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2310     QVERIFY(!myText->truncated());
2311     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2312     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2313
2314     myText->setElideMode(QQuickText::ElideNone);
2315     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2316 }
2317
2318 void tst_qquicktext::fontSizeModeMultiline_data()
2319 {
2320     QTest::addColumn<QString>("text");
2321     QTest::newRow("plain") << "The quick red fox jumped\n over the lazy brown dog";
2322     QTest::newRow("styledtext") << "<b>The quick red fox jumped<br/> over the lazy brown dog</b>";
2323 }
2324
2325 void tst_qquicktext::fontSizeModeMultiline()
2326 {
2327     QFETCH(QString, text);
2328
2329     QScopedPointer<QQuickView> canvas(createView(testFile("fontSizeMode.qml")));
2330     canvas->show();
2331
2332     QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
2333     QVERIFY(myText != 0);
2334
2335     myText->setText(text);
2336     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2337
2338     qreal originalWidth = myText->contentWidth();
2339     qreal originalHeight = myText->contentHeight();
2340     QCOMPARE(myText->lineCount(), 2);
2341
2342     // The original text unwrapped should exceed the width and height of the item.
2343     QVERIFY(originalWidth > myText->width());
2344     QVERIFY(originalHeight > myText->height());
2345
2346     QFont font = myText->font();
2347     font.setPixelSize(64);
2348
2349     myText->setFont(font);
2350     myText->setFontSizeMode(QQuickText::HorizontalFit);
2351     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2352     // Font size reduced to fit within the width of the item.
2353     QCOMPARE(myText->lineCount(), 2);
2354     qreal horizontalFitWidth = myText->contentWidth();
2355     qreal horizontalFitHeight = myText->contentHeight();
2356     QVERIFY(horizontalFitWidth <= myText->width() + 2); // rounding
2357     QVERIFY(horizontalFitHeight > myText->height());
2358
2359     // Right eliding will remove the last line
2360     myText->setElideMode(QQuickText::ElideRight);
2361     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2362     QVERIFY(myText->truncated());
2363     QCOMPARE(myText->lineCount(), 1);
2364     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2365     QVERIFY(myText->contentHeight() <= myText->height() + 2);
2366
2367     // Left or middle eliding wont have any effect.
2368     myText->setElideMode(QQuickText::ElideLeft);
2369     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2370     QVERIFY(!myText->truncated());
2371     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2372     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2373
2374     myText->setElideMode(QQuickText::ElideMiddle);
2375     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2376     QVERIFY(!myText->truncated());
2377     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2378     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2379
2380     myText->setElideMode(QQuickText::ElideNone);
2381     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2382
2383     myText->setFontSizeMode(QQuickText::VerticalFit);
2384     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2385     // Font size reduced to fit within the height of the item.
2386     qreal verticalFitWidth = myText->contentWidth();
2387     qreal verticalFitHeight = myText->contentHeight();
2388     QVERIFY(verticalFitWidth <= myText->width() + 2);
2389     QVERIFY(verticalFitHeight <= myText->height() + 2);
2390
2391     // Elide will have no effect.
2392     myText->setElideMode(QQuickText::ElideRight);
2393     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2394     QVERIFY(!myText->truncated());
2395     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2396     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2397     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2398
2399     myText->setElideMode(QQuickText::ElideLeft);
2400     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2401     QVERIFY(!myText->truncated());
2402     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2403     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2404
2405     myText->setElideMode(QQuickText::ElideMiddle);
2406     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2407     QVERIFY(!myText->truncated());
2408     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2409     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2410
2411     myText->setElideMode(QQuickText::ElideNone);
2412     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2413
2414     myText->setFontSizeMode(QQuickText::Fit);
2415     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2416     // Should be the same as VerticalFit with no wrapping.
2417     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2418     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2419
2420     // Elide won't affect the size with Fit.
2421     myText->setElideMode(QQuickText::ElideRight);
2422     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2423     QVERIFY(!myText->truncated());
2424     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2425     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2426
2427     myText->setElideMode(QQuickText::ElideLeft);
2428     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2429     QVERIFY(!myText->truncated());
2430     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2431     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2432
2433     myText->setElideMode(QQuickText::ElideMiddle);
2434     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2435     QVERIFY(!myText->truncated());
2436     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2437     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2438
2439     myText->setElideMode(QQuickText::ElideNone);
2440     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2441
2442     myText->setFontSizeMode(QQuickText::FixedSize);
2443     myText->setWrapMode(QQuickText::Wrap);
2444     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2445
2446     originalWidth = myText->contentWidth();
2447     originalHeight = myText->contentHeight();
2448
2449     // The original text wrapped should exceed the height of the item.
2450     QVERIFY(originalWidth <= myText->width() + 2);
2451     QVERIFY(originalHeight > myText->height());
2452
2453     myText->setFontSizeMode(QQuickText::HorizontalFit);
2454     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2455     // HorizontalFit should reduce the font size to minimize wrapping, which brings it back to the
2456     // same size as without text wrapping.
2457     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2458     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2459
2460     // Text will be elided vertically with HorizontalFit
2461     myText->setElideMode(QQuickText::ElideRight);
2462     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2463     QVERIFY(myText->truncated());
2464     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2465     QVERIFY(myText->contentHeight() <= myText->height() + 2);
2466
2467     myText->setElideMode(QQuickText::ElideNone);
2468     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2469
2470     myText->setFontSizeMode(QQuickText::VerticalFit);
2471     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2472     // VerticalFit should reduce the size to the wrapped text within the vertical height.
2473     verticalFitHeight = myText->contentHeight();
2474     verticalFitWidth = myText->contentWidth();
2475     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2476     QVERIFY(verticalFitHeight <= myText->height() + 2);
2477     QVERIFY(verticalFitHeight < originalHeight);
2478
2479     // Elide won't affect the height or width of a wrapped text with VerticalFit.
2480     myText->setElideMode(QQuickText::ElideRight);
2481     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2482     QVERIFY(!myText->truncated());
2483     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2484     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2485
2486     myText->setElideMode(QQuickText::ElideNone);
2487     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2488
2489     myText->setFontSizeMode(QQuickText::Fit);
2490     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2491     // Should be the same as VerticalFit with wrapping.
2492     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2493     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2494
2495     // Elide won't affect the size with Fit.
2496     myText->setElideMode(QQuickText::ElideRight);
2497     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2498     QVERIFY(!myText->truncated());
2499     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2500     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2501
2502     myText->setElideMode(QQuickText::ElideNone);
2503     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2504
2505     myText->setFontSizeMode(QQuickText::FixedSize);
2506     myText->setMaximumLineCount(2);
2507     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2508
2509     // The original text wrapped should exceed the height of the item.
2510     QVERIFY(originalWidth <= myText->width() + 2);
2511     QVERIFY(originalHeight > myText->height());
2512
2513     myText->setFontSizeMode(QQuickText::HorizontalFit);
2514     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2515     // HorizontalFit should reduce the font size to minimize wrapping, which brings it back to the
2516     // same size as without text wrapping.
2517     QCOMPARE(myText->contentWidth(), horizontalFitWidth);
2518     QCOMPARE(myText->contentHeight(), horizontalFitHeight);
2519
2520     // Elide won't affect the size with HorizontalFit.
2521     myText->setElideMode(QQuickText::ElideRight);
2522     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2523     QVERIFY(myText->truncated());
2524     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2525     QVERIFY(myText->contentHeight() <= myText->height() + 2);
2526
2527     myText->setElideMode(QQuickText::ElideNone);
2528     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2529
2530     myText->setFontSizeMode(QQuickText::VerticalFit);
2531     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2532     // VerticalFit should reduce the size to the wrapped text within the vertical height.
2533     verticalFitHeight = myText->contentHeight();
2534     verticalFitWidth = myText->contentWidth();
2535     QVERIFY(myText->contentWidth() <= myText->width() + 2);
2536     QVERIFY(verticalFitHeight <= myText->height() + 2);
2537     QVERIFY(verticalFitHeight < originalHeight);
2538
2539     // Elide won't affect the height or width of a wrapped text with VerticalFit.
2540     myText->setElideMode(QQuickText::ElideRight);
2541     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2542     QVERIFY(!myText->truncated());
2543     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2544     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2545
2546     myText->setElideMode(QQuickText::ElideNone);
2547     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2548
2549     myText->setFontSizeMode(QQuickText::Fit);
2550     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2551     // Should be the same as VerticalFit with wrapping.
2552     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2553     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2554
2555     // Elide won't affect the size with Fit.
2556     myText->setElideMode(QQuickText::ElideRight);
2557     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2558     QVERIFY(!myText->truncated());
2559     QCOMPARE(myText->contentWidth(), verticalFitWidth);
2560     QCOMPARE(myText->contentHeight(), verticalFitHeight);
2561
2562     myText->setElideMode(QQuickText::ElideNone);
2563     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2564 }
2565
2566 void tst_qquicktext::multilengthStrings_data()
2567 {
2568     QTest::addColumn<QString>("source");
2569     QTest::newRow("No Wrap") << testFile("multilengthStrings.qml");
2570     QTest::newRow("Wrap") << testFile("multilengthStringsWrapped.qml");
2571 }
2572
2573 void tst_qquicktext::multilengthStrings()
2574 {
2575     QFETCH(QString, source);
2576
2577     QScopedPointer<QQuickView> canvas(createView(source));
2578     canvas->show();
2579
2580     QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
2581     QVERIFY(myText != 0);
2582
2583     const QString longText = "the quick brown fox jumped over the lazy dog";
2584     const QString mediumText = "the brown fox jumped over the dog";
2585     const QString shortText = "fox jumped dog";
2586
2587     myText->setText(longText);
2588     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2589     const qreal longWidth = myText->contentWidth();
2590     const qreal longHeight = myText->contentHeight();
2591
2592     myText->setText(mediumText);
2593     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2594     const qreal mediumWidth = myText->contentWidth();
2595     const qreal mediumHeight = myText->contentHeight();
2596
2597     myText->setText(shortText);
2598     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2599     const qreal shortWidth = myText->contentWidth();
2600     const qreal shortHeight = myText->contentHeight();
2601
2602     myText->setElideMode(QQuickText::ElideRight);
2603     myText->setText(longText + QLatin1Char('\x9c') + mediumText + QLatin1Char('\x9c') + shortText);
2604
2605     myText->setSize(QSizeF(longWidth, longHeight));
2606     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2607
2608     QCOMPARE(myText->contentWidth(), longWidth);
2609     QCOMPARE(myText->contentHeight(), longHeight);
2610     QCOMPARE(myText->truncated(), false);
2611
2612     myText->setSize(QSizeF(mediumWidth, mediumHeight));
2613     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2614
2615     QCOMPARE(myText->contentWidth(), mediumWidth);
2616     QCOMPARE(myText->contentHeight(), mediumHeight);
2617     QCOMPARE(myText->truncated(), true);
2618
2619     myText->setSize(QSizeF(shortWidth, shortHeight));
2620     QTRY_COMPARE(QQuickItemPrivate::get(myText)->polishScheduled, false);
2621
2622     QCOMPARE(myText->contentWidth(), shortWidth);
2623     QCOMPARE(myText->contentHeight(), shortHeight);
2624     QCOMPARE(myText->truncated(), true);
2625 }
2626
2627 void tst_qquicktext::fontFormatSizes_data()
2628 {
2629     QTest::addColumn<QString>("text");
2630     QTest::addColumn<QString>("textWithTag");
2631     QTest::addColumn<bool>("fontIsBigger");
2632
2633     QTest::newRow("fs1") << "Hello world!" << "Hello <font size=\"1\">world</font>!" << false;
2634     QTest::newRow("fs2") << "Hello world!" << "Hello <font size=\"2\">world</font>!" << false;
2635     QTest::newRow("fs3") << "Hello world!" << "Hello <font size=\"3\">world</font>!" << false;
2636     QTest::newRow("fs4") << "Hello world!" << "Hello <font size=\"4\">world</font>!" << true;
2637     QTest::newRow("fs5") << "Hello world!" << "Hello <font size=\"5\">world</font>!" << true;
2638     QTest::newRow("fs6") << "Hello world!" << "Hello <font size=\"6\">world</font>!" << true;
2639     QTest::newRow("fs7") << "Hello world!" << "Hello <font size=\"7\">world</font>!" << true;
2640     QTest::newRow("h1") << "This is<br/>a font<br/> size test." << "This is <h1>a font</h1> size test." << true;
2641     QTest::newRow("h2") << "This is<br/>a font<br/> size test." << "This is <h2>a font</h2> size test." << true;
2642     QTest::newRow("h3") << "This is<br/>a font<br/> size test." << "This is <h3>a font</h3> size test." << true;
2643     QTest::newRow("h4") << "This is<br/>a font<br/> size test." << "This is <h4>a font</h4> size test." << true;
2644     QTest::newRow("h5") << "This is<br/>a font<br/> size test." << "This is <h5>a font</h5> size test." << false;
2645     QTest::newRow("h6") << "This is<br/>a font<br/> size test." << "This is <h6>a font</h6> size test." << false;
2646 }
2647
2648 void tst_qquicktext::fontFormatSizes()
2649 {
2650     QFETCH(QString, text);
2651     QFETCH(QString, textWithTag);
2652     QFETCH(bool, fontIsBigger);
2653
2654     QQuickView *view = new QQuickView;
2655     {
2656         view->setSource(testFileUrl("pointFontSizes.qml"));
2657         view->show();
2658
2659         QQuickText *qtext = view->rootObject()->findChild<QQuickText*>("text");
2660         QQuickText *qtextWithTag = view->rootObject()->findChild<QQuickText*>("textWithTag");
2661         QVERIFY(qtext != 0);
2662         QVERIFY(qtextWithTag != 0);
2663
2664         qtext->setText(text);
2665         qtextWithTag->setText(textWithTag);
2666
2667         for (int size = 6; size < 100; size += 4) {
2668             view->rootObject()->setProperty("pointSize", size);
2669             if (fontIsBigger)
2670                 QVERIFY(qtext->height() <= qtextWithTag->height());
2671             else
2672                 QVERIFY(qtext->height() >= qtextWithTag->height());
2673         }
2674     }
2675
2676     {
2677         view->setSource(testFileUrl("pixelFontSizes.qml"));
2678         QQuickText *qtext = view->rootObject()->findChild<QQuickText*>("text");
2679         QQuickText *qtextWithTag = view->rootObject()->findChild<QQuickText*>("textWithTag");
2680         QVERIFY(qtext != 0);
2681         QVERIFY(qtextWithTag != 0);
2682
2683         qtext->setText(text);
2684         qtextWithTag->setText(textWithTag);
2685
2686         for (int size = 6; size < 100; size += 4) {
2687             view->rootObject()->setProperty("pixelSize", size);
2688             if (fontIsBigger)
2689                 QVERIFY(qtext->height() <= qtextWithTag->height());
2690             else
2691                 QVERIFY(qtext->height() >= qtextWithTag->height());
2692         }
2693     }
2694     delete view;
2695 }
2696
2697 typedef qreal (*ExpectedBaseline)(QQuickText *item);
2698 Q_DECLARE_METATYPE(ExpectedBaseline)
2699
2700 static qreal expectedBaselineTop(QQuickText *item)
2701 {
2702     QFontMetricsF fm(item->font());
2703     return fm.ascent();
2704 }
2705
2706 static qreal expectedBaselineBottom(QQuickText *item)
2707 {
2708     QFontMetricsF fm(item->font());
2709     return item->height() - item->contentHeight() + fm.ascent();
2710 }
2711
2712 static qreal expectedBaselineCenter(QQuickText *item)
2713 {
2714     QFontMetricsF fm(item->font());
2715     return ((item->height() - item->contentHeight()) / 2) + fm.ascent();
2716 }
2717
2718 static qreal expectedBaselineBold(QQuickText *item)
2719 {
2720     QFont font = item->font();
2721     font.setBold(true);
2722     QFontMetricsF fm(font);
2723     return fm.ascent();
2724 }
2725
2726 static qreal expectedBaselineImage(QQuickText *item)
2727 {
2728     QFontMetricsF fm(item->font());
2729     // The line is positioned so the bottom of the line is aligned with the bottom of the image,
2730     // or image height - line height and the baseline is line position + ascent.  Because
2731     // QTextLine's height is rounded up this can give slightly different results to image height
2732     // - descent.
2733     return 181 - qCeil(fm.height()) + fm.ascent();
2734 }
2735
2736 static qreal expectedBaselineCustom(QQuickText *item)
2737 {
2738     QFontMetricsF fm(item->font());
2739     return 16 + fm.ascent();
2740 }
2741
2742 static qreal expectedBaselineScaled(QQuickText *item)
2743 {
2744     QFont font = item->font();
2745     QTextLayout layout(item->text().replace(QLatin1Char('\n'), QChar::LineSeparator));
2746     do {
2747         layout.setFont(font);
2748         qreal width = 0;
2749         layout.beginLayout();
2750         for (QTextLine line = layout.createLine(); line.isValid(); line = layout.createLine()) {
2751             line.setLineWidth(FLT_MAX);
2752             width = qMax(line.naturalTextWidth(), width);
2753         }
2754         layout.endLayout();
2755
2756         if (width < item->width()) {
2757             QFontMetricsF fm(layout.font());
2758             return fm.ascent();
2759         }
2760         font.setPointSize(font.pointSize() - 1);
2761     } while (font.pointSize() > 0);
2762     return 0;
2763 }
2764
2765 static qreal expectedBaselineFixedBottom(QQuickText *item)
2766 {
2767     QFontMetricsF fm(item->font());
2768     qreal dy = item->text().contains(QLatin1Char('\n'))
2769             ? 160
2770             : 180;
2771     return dy + fm.ascent();
2772 }
2773
2774 static qreal expectedBaselineProportionalBottom(QQuickText *item)
2775 {
2776     QFontMetricsF fm(item->font());
2777     qreal dy = item->text().contains(QLatin1Char('\n'))
2778             ? 200 - (qCeil(fm.height()) * 3)
2779             : 200 - (qCeil(fm.height()) * 1.5);
2780     return dy + fm.ascent();
2781 }
2782
2783 void tst_qquicktext::baselineOffset_data()
2784 {
2785     qRegisterMetaType<ExpectedBaseline>();
2786     QTest::addColumn<QString>("text");
2787     QTest::addColumn<QString>("wrappedText");
2788     QTest::addColumn<QByteArray>("bindings");
2789     QTest::addColumn<ExpectedBaseline>("expectedBaseline");
2790     QTest::addColumn<ExpectedBaseline>("expectedBaselineEmpty");
2791
2792     QTest::newRow("top align")
2793             << "hello world"
2794             << "hello\nworld"
2795             << QByteArray("height: 200; verticalAlignment: Text.AlignTop")
2796             << &expectedBaselineTop
2797             << &expectedBaselineTop;
2798     QTest::newRow("bottom align")
2799             << "hello world"
2800             << "hello\nworld"
2801             << QByteArray("height: 200; verticalAlignment: Text.AlignBottom")
2802             << &expectedBaselineBottom
2803             << &expectedBaselineBottom;
2804     QTest::newRow("center align")
2805             << "hello world"
2806             << "hello\nworld"
2807             << QByteArray("height: 200; verticalAlignment: Text.AlignVCenter")
2808             << &expectedBaselineCenter
2809             << &expectedBaselineCenter;
2810
2811     QTest::newRow("bold")
2812             << "<b>hello world</b>"
2813             << "<b>hello<br/>world</b>"
2814             << QByteArray("height: 200")
2815             << &expectedBaselineTop
2816             << &expectedBaselineBold;
2817
2818     QTest::newRow("richText")
2819             << "<b>hello world</b>"
2820             << "<b>hello<br/>world</b>"
2821             << QByteArray("height: 200; textFormat: Text.RichText")
2822             << &expectedBaselineTop
2823             << &expectedBaselineTop;
2824
2825     QTest::newRow("elided")
2826             << "hello world"
2827             << "hello\nworld"
2828             << QByteArray("width: 20; height: 8; elide: Text.ElideRight")
2829             << &expectedBaselineTop
2830             << &expectedBaselineTop;
2831
2832     QTest::newRow("elided bottom align")
2833             << "hello world"
2834             << "hello\nworld!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
2835             << QByteArray("width: 200; height: 200; elide: Text.ElideRight; verticalAlignment: Text.AlignBottom")
2836             << &expectedBaselineBottom
2837             << &expectedBaselineBottom;
2838
2839     QTest::newRow("image")
2840             << "hello <img src=\"images/heart200.png\" /> world"
2841             << "hello <img src=\"images/heart200.png\" /><br/>world"
2842             << QByteArray("height: 200\n; baseUrl: \"") + testFileUrl("reference").toEncoded() + QByteArray("\"")
2843             << &expectedBaselineImage
2844             << &expectedBaselineTop;
2845
2846     QTest::newRow("customLine")
2847             << "hello world"
2848             << "hello\nworld"
2849             << QByteArray("height: 200; onLineLaidOut: line.y += 16")
2850             << &expectedBaselineCustom
2851             << &expectedBaselineCustom;
2852
2853     QTest::newRow("scaled font")
2854             << "hello world"
2855             << "hello\nworld"
2856             << QByteArray("width: 200; minimumPointSize: 1; font.pointSize: 64; fontSizeMode: Text.HorizontalFit")
2857             << &expectedBaselineScaled
2858             << &expectedBaselineTop;
2859
2860     QTest::newRow("fixed line height top align")
2861             << "hello world"
2862             << "hello\nworld"
2863             << QByteArray("height: 200; lineHeightMode: Text.FixedHeight; lineHeight: 20; verticalAlignment: Text.AlignTop")
2864             << &expectedBaselineTop
2865             << &expectedBaselineTop;
2866
2867     QTest::newRow("fixed line height bottom align")
2868             << "hello world"
2869             << "hello\nworld"
2870             << QByteArray("height: 200; lineHeightMode: Text.FixedHeight; lineHeight: 20; verticalAlignment: Text.AlignBottom")
2871             << &expectedBaselineFixedBottom
2872             << &expectedBaselineFixedBottom;
2873
2874     QTest::newRow("proportional line height top align")
2875             << "hello world"
2876             << "hello\nworld"
2877             << QByteArray("height: 200; lineHeightMode: Text.ProportionalHeight; lineHeight: 1.5; verticalAlignment: Text.AlignTop")
2878             << &expectedBaselineTop
2879             << &expectedBaselineTop;
2880
2881     QTest::newRow("proportional line height bottom align")
2882             << "hello world"
2883             << "hello\nworld"
2884             << QByteArray("height: 200; lineHeightMode: Text.ProportionalHeight; lineHeight: 1.5; verticalAlignment: Text.AlignBottom")
2885             << &expectedBaselineProportionalBottom
2886             << &expectedBaselineProportionalBottom;
2887 }
2888
2889 void tst_qquicktext::baselineOffset()
2890 {
2891     QFETCH(QString, text);
2892     QFETCH(QString, wrappedText);
2893     QFETCH(QByteArray, bindings);
2894     QFETCH(ExpectedBaseline, expectedBaseline);
2895     QFETCH(ExpectedBaseline, expectedBaselineEmpty);
2896
2897     QQmlComponent component(&engine);
2898     component.setData(
2899             "import QtQuick 2.0\n"
2900             "Text {\n"
2901                 + bindings + "\n"
2902             "}", QUrl());
2903
2904     QScopedPointer<QObject> object(component.create());
2905
2906     QQuickText *item = qobject_cast<QQuickText *>(object.data());
2907     QVERIFY(item);
2908
2909     {
2910         qreal baseline = expectedBaselineEmpty(item);
2911
2912         QCOMPARE(item->baselineOffset(), baseline);
2913
2914         item->setText(text);
2915         if (expectedBaseline != expectedBaselineEmpty)
2916             baseline = expectedBaseline(item);
2917
2918         QCOMPARE(item->baselineOffset(), baseline);
2919
2920         item->setText(wrappedText);
2921         QCOMPARE(item->baselineOffset(), expectedBaseline(item));
2922     }
2923
2924     QFont font = item->font();
2925     font.setPointSize(font.pointSize() + 8);
2926
2927     {
2928         QCOMPARE(item->baselineOffset(), expectedBaseline(item));
2929
2930         item->setText(text);
2931         qreal baseline = expectedBaseline(item);
2932         QCOMPARE(item->baselineOffset(), baseline);
2933
2934         item->setText(QString());
2935         if (expectedBaselineEmpty != expectedBaseline)
2936             baseline = expectedBaselineEmpty(item);
2937
2938         QCOMPARE(item->baselineOffset(), baseline);
2939     }
2940 }
2941
2942 QTEST_MAIN(tst_qquicktext)
2943
2944 #include "tst_qquicktext.moc"