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