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