1428f327128ac3ac851767d651c35c5fd9ee08fa
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qsgtext / tst_qsgtext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <qtest.h>
42 #include <QTextDocument>
43 #include <QtDeclarative/qdeclarativeengine.h>
44 #include <QtDeclarative/qdeclarativecomponent.h>
45 #include <private/qsgtext_p.h>
46 #include <private/qsgtext_p_p.h>
47 #include <private/qdeclarativevaluetype_p.h>
48 #include <private/qsgdistancefieldglyphcache_p.h>
49 #include <QFontMetrics>
50 #include <QGraphicsSceneMouseEvent>
51 #include <qmath.h>
52 #include <QSGView>
53 #include <private/qapplication_p.h>
54 #include <limits.h>
55 #include <QtGui/QMouseEvent>
56 #include "../../../shared/util.h"
57 #include "testhttpserver.h"
58 #include <QtOpenGL/QGLShaderProgram>
59
60 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
61
62 class tst_qsgtext : public QObject
63 {
64     Q_OBJECT
65 public:
66     tst_qsgtext();
67
68 private slots:
69     void initTestCase();
70     void cleanupTestCase();
71     void text();
72     void width();
73     void wrap();
74     void elide();
75     void textFormat();
76
77     void alignments_data();
78     void alignments();
79
80     void embeddedImages_data();
81     void embeddedImages();
82
83     void lineCount();
84     void lineHeight();
85
86     // ### these tests may be trivial    
87     void horizontalAlignment();
88     void horizontalAlignment_RightToLeft();
89     void verticalAlignment();
90     void font();
91     void style();
92     void color();
93     void smooth();
94
95     // QDeclarativeFontValueType
96     void weight();
97     void underline();
98     void overline();
99     void strikeout();
100     void capitalization();
101     void letterSpacing();
102     void wordSpacing();
103
104     void clickLink();
105
106     void implicitSize_data();
107     void implicitSize();
108
109     void lineLaidOut();
110
111
112 private:
113     QStringList standard;
114     QStringList richText;
115
116     QStringList horizontalAlignmentmentStrings;
117     QStringList verticalAlignmentmentStrings;
118
119     QList<Qt::Alignment> verticalAlignmentments;
120     QList<Qt::Alignment> horizontalAlignmentments;
121
122     QStringList styleStrings;
123     QList<QSGText::TextStyle> styles;
124
125     QStringList colorStrings;
126
127     QDeclarativeEngine engine;
128
129     QSGView *createView(const QString &filename);
130 };
131 void tst_qsgtext::initTestCase()
132 {
133 }
134
135 void tst_qsgtext::cleanupTestCase()
136 {
137
138 }
139 tst_qsgtext::tst_qsgtext()
140 {
141     standard << "the quick brown fox jumped over the lazy dog"
142             << "the quick brown fox\n jumped over the lazy dog";
143
144     richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
145             << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
146
147     horizontalAlignmentmentStrings << "AlignLeft"
148             << "AlignRight"
149             << "AlignHCenter";
150
151     verticalAlignmentmentStrings << "AlignTop"
152             << "AlignBottom"
153             << "AlignVCenter";
154
155     horizontalAlignmentments << Qt::AlignLeft
156             << Qt::AlignRight
157             << Qt::AlignHCenter;
158
159     verticalAlignmentments << Qt::AlignTop
160             << Qt::AlignBottom
161             << Qt::AlignVCenter;
162
163     styleStrings << "Normal"
164             << "Outline"
165             << "Raised"
166             << "Sunken";
167
168     styles << QSGText::Normal
169             << QSGText::Outline
170             << QSGText::Raised
171             << QSGText::Sunken;
172
173     colorStrings << "aliceblue"
174             << "antiquewhite"
175             << "aqua"
176             << "darkkhaki"
177             << "darkolivegreen"
178             << "dimgray"
179             << "palevioletred"
180             << "lightsteelblue"
181             << "#000000"
182             << "#AAAAAA"
183             << "#FFFFFF"
184             << "#2AC05F";
185     //
186     // need a different test to do alpha channel test
187     // << "#AA0011DD"
188     // << "#00F16B11";
189     //
190 }
191
192 QSGView *tst_qsgtext::createView(const QString &filename)
193 {
194     QSGView *canvas = new QSGView(0);
195
196     canvas->setSource(QUrl::fromLocalFile(filename));
197     return canvas;
198 }
199
200 void tst_qsgtext::text()
201 {
202     {
203         QDeclarativeComponent textComponent(&engine);
204         textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
205         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
206
207         QVERIFY(textObject != 0);
208         QCOMPARE(textObject->text(), QString(""));
209         QVERIFY(textObject->width() == 0);
210
211         delete textObject;
212     }
213
214     for (int i = 0; i < standard.size(); i++)
215     {
216         QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
217         QDeclarativeComponent textComponent(&engine);
218         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
219
220         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
221
222         QVERIFY(textObject != 0);
223         QCOMPARE(textObject->text(), standard.at(i));
224         QVERIFY(textObject->width() > 0);
225
226         delete textObject;
227     }
228
229     for (int i = 0; i < richText.size(); i++)
230     {
231         QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
232         QDeclarativeComponent textComponent(&engine);
233         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
234         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
235
236         QVERIFY(textObject != 0);
237         QString expected = richText.at(i);
238         QCOMPARE(textObject->text(), expected.replace("\\\"", "\""));
239         QVERIFY(textObject->width() > 0);
240
241         delete textObject;
242     }
243 }
244
245 void tst_qsgtext::width()
246 {
247     // uses Font metrics to find the width for standard and document to find the width for rich
248     {
249         QDeclarativeComponent textComponent(&engine);
250         textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
251         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
252
253         QVERIFY(textObject != 0);
254         QCOMPARE(textObject->width(), 0.);
255
256         delete textObject;
257     }
258
259     bool requiresUnhintedMetrics = !qmlDisableDistanceField();
260
261     for (int i = 0; i < standard.size(); i++)
262     {
263         QVERIFY(!Qt::mightBeRichText(standard.at(i))); // self-test
264
265         QFont f;
266         qreal metricWidth = 0.0;
267
268         if (requiresUnhintedMetrics) {
269             QString s = standard.at(i);
270             s.replace(QLatin1Char('\n'), QChar::LineSeparator);
271
272             QTextLayout layout(s);
273             layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
274             {
275                 QTextOption option;
276                 option.setUseDesignMetrics(true);
277                 layout.setTextOption(option);
278             }
279
280             layout.beginLayout();
281             forever {
282                 QTextLine line = layout.createLine();
283                 if (!line.isValid())
284                     break;
285             }
286
287             layout.endLayout();
288
289             metricWidth = qCeil(layout.boundingRect().width());
290         } else {
291             QFontMetricsF fm(f);
292             qreal metricWidth = fm.size(Qt::TextExpandTabs && Qt::TextShowMnemonic, standard.at(i)).width();
293             metricWidth = qCeil(metricWidth);
294         }
295
296         QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
297         QDeclarativeComponent textComponent(&engine);
298         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
299         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
300
301         QVERIFY(textObject != 0);
302         QVERIFY(textObject->boundingRect().width() > 0);
303         QCOMPARE(textObject->width(), qreal(metricWidth));
304         QVERIFY(textObject->textFormat() == QSGText::AutoText); // setting text doesn't change format
305
306         delete textObject;
307     }
308
309     for (int i = 0; i < richText.size(); i++)
310     {
311         QVERIFY(Qt::mightBeRichText(richText.at(i))); // self-test
312
313         QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\"; textFormat: Text.RichText }";
314         QDeclarativeComponent textComponent(&engine);
315         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
316         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
317         QVERIFY(textObject != 0);
318
319         QSGTextPrivate *textPrivate = QSGTextPrivate::get(textObject);
320         QVERIFY(textPrivate != 0);
321
322         QTextDocument *doc = textPrivate->textDocument();
323         QVERIFY(doc != 0);
324
325         QCOMPARE(int(textObject->width()), int(doc->idealWidth()));
326         QVERIFY(textObject->textFormat() == QSGText::RichText);
327
328         delete textObject;
329     }
330 }
331
332 void tst_qsgtext::wrap()
333 {
334     int textHeight = 0;
335     // for specified width and wrap set true
336     {
337         QDeclarativeComponent textComponent(&engine);
338         textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; wrapMode: Text.WordWrap; width: 300 }", QUrl::fromLocalFile(""));
339         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
340         textHeight = textObject->height();
341
342         QVERIFY(textObject != 0);
343         QVERIFY(textObject->wrapMode() == QSGText::WordWrap);
344         QCOMPARE(textObject->width(), 300.);
345
346         delete textObject;
347     }
348
349     for (int i = 0; i < standard.size(); i++)
350     {
351         QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + standard.at(i) + "\" }";
352         QDeclarativeComponent textComponent(&engine);
353         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
354         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
355
356         QVERIFY(textObject != 0);
357         QCOMPARE(textObject->width(), 30.);
358         QVERIFY(textObject->height() > textHeight);
359
360         int oldHeight = textObject->height();
361         textObject->setWidth(100);
362         QVERIFY(textObject->height() < oldHeight);
363
364         delete textObject;
365     }
366
367     for (int i = 0; i < richText.size(); i++)
368     {
369         QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + richText.at(i) + "\" }";
370         QDeclarativeComponent textComponent(&engine);
371         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
372         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
373
374         QVERIFY(textObject != 0);
375         QCOMPARE(textObject->width(), 30.);
376         QVERIFY(textObject->height() > textHeight);
377
378         qreal oldHeight = textObject->height();
379         textObject->setWidth(100);
380         QVERIFY(textObject->height() < oldHeight);
381
382         delete textObject;
383     }
384
385     // richtext again with a fixed height
386     for (int i = 0; i < richText.size(); i++)
387     {
388         QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; height: 50; text: \"" + richText.at(i) + "\" }";
389         QDeclarativeComponent textComponent(&engine);
390         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
391         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
392
393         QVERIFY(textObject != 0);
394         QCOMPARE(textObject->width(), 30.);
395         QVERIFY(textObject->implicitHeight() > textHeight);
396
397         qreal oldHeight = textObject->implicitHeight();
398         textObject->setWidth(100);
399         QVERIFY(textObject->implicitHeight() < oldHeight);
400
401         delete textObject;
402     }
403 }
404
405 void tst_qsgtext::elide()
406 {
407     for (QSGText::TextElideMode m = QSGText::ElideLeft; m<=QSGText::ElideNone; m=QSGText::TextElideMode(int(m)+1)) {
408         const char* elidename[]={"ElideLeft", "ElideRight", "ElideMiddle", "ElideNone"};
409         QString elide = "elide: Text." + QString(elidename[int(m)]) + ";";
410
411         // XXX Poor coverage.
412
413         {
414             QDeclarativeComponent textComponent(&engine);
415             textComponent.setData(("import QtQuick 2.0\nText { text: \"\"; "+elide+" width: 100 }").toLatin1(), QUrl::fromLocalFile(""));
416             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
417
418             QCOMPARE(textObject->elideMode(), m);
419             QCOMPARE(textObject->width(), 100.);
420
421             delete textObject;
422         }
423
424         for (int i = 0; i < standard.size(); i++)
425         {
426             QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + standard.at(i) + "\" }";
427             QDeclarativeComponent textComponent(&engine);
428             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
429             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
430
431             QCOMPARE(textObject->elideMode(), m);
432             QCOMPARE(textObject->width(), 100.);
433
434             delete textObject;
435         }
436
437         // richtext - does nothing
438         for (int i = 0; i < richText.size(); i++)
439         {
440             QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + richText.at(i) + "\" }";
441             QDeclarativeComponent textComponent(&engine);
442             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
443             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
444
445             QCOMPARE(textObject->elideMode(), m);
446             QCOMPARE(textObject->width(), 100.);
447
448             delete textObject;
449         }
450     }
451 }
452
453 void tst_qsgtext::textFormat()
454 {
455     {
456         QDeclarativeComponent textComponent(&engine);
457         textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
458         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
459
460         QVERIFY(textObject != 0);
461         QVERIFY(textObject->textFormat() == QSGText::RichText);
462
463         QSGTextPrivate *textPrivate = QSGTextPrivate::get(textObject);
464         QVERIFY(textPrivate != 0);
465         QVERIFY(textPrivate->richText == true);
466
467         delete textObject;
468     }
469     {
470         QDeclarativeComponent textComponent(&engine);
471         textComponent.setData("import QtQuick 2.0\nText { text: \"<b>Hello</b>\" }", QUrl::fromLocalFile(""));
472         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
473
474         QVERIFY(textObject != 0);
475         QVERIFY(textObject->textFormat() == QSGText::AutoText);
476
477         QSGTextPrivate *textPrivate = QSGTextPrivate::get(textObject);
478         QVERIFY(textPrivate != 0);
479         QVERIFY(textPrivate->styledText == true);
480
481         delete textObject;
482     }
483     {
484         QDeclarativeComponent textComponent(&engine);
485         textComponent.setData("import QtQuick 2.0\nText { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
486         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
487
488         QVERIFY(textObject != 0);
489         QVERIFY(textObject->textFormat() == QSGText::PlainText);
490
491         delete textObject;
492     }
493 }
494
495
496 void tst_qsgtext::alignments_data()
497 {
498     QTest::addColumn<int>("hAlign");
499     QTest::addColumn<int>("vAlign");
500     QTest::addColumn<QString>("expectfile");
501
502     QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << SRCDIR "/data/alignments_lt.png";
503     QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << SRCDIR "/data/alignments_rt.png";
504     QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << SRCDIR "/data/alignments_ct.png";
505
506     QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << SRCDIR "/data/alignments_lb.png";
507     QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << SRCDIR "/data/alignments_rb.png";
508     QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << SRCDIR "/data/alignments_cb.png";
509
510     QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << SRCDIR "/data/alignments_lc.png";
511     QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << SRCDIR "/data/alignments_rc.png";
512     QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << SRCDIR "/data/alignments_cc.png";
513 }
514
515
516 void tst_qsgtext::alignments()
517 {
518
519     QSKIP("Text alignment pixmap comparison tests will not work with scenegraph", SkipAll);
520 #if (0)// No widgets in scenegraph
521     QFETCH(int, hAlign);
522     QFETCH(int, vAlign);
523     QFETCH(QString, expectfile);
524
525 #ifdef Q_WS_X11
526     // Font-specific, but not likely platform-specific, so only test on one platform
527     QFont fn;
528     fn.setRawName("-misc-fixed-medium-r-*-*-8-*-*-*-*-*-*-*");
529     QApplication::setFont(fn);
530 #endif
531
532     QSGView *canvas = createView(SRCDIR "/data/alignments.qml");
533
534     canvas->show();
535     canvas->requestActivateWindow();
536     QTest::qWait(50);
537     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
538
539     QObject *ob = canvas->rootObject();
540     QVERIFY(ob != 0);
541     ob->setProperty("horizontalAlignment",hAlign);
542     ob->setProperty("verticalAlignment",vAlign);
543     QTRY_COMPARE(ob->property("running").toBool(),false);
544     QImage actual(canvas->width(), canvas->height(), QImage::Format_RGB32);
545     actual.fill(qRgb(255,255,255));
546     QPainter p(&actual);
547     canvas->render(&p);
548
549     QImage expect(expectfile);
550
551 #ifdef Q_WS_X11
552     // Font-specific, but not likely platform-specific, so only test on one platform
553     if (QApplicationPrivate::graphics_system_name == "raster" || QApplicationPrivate::graphics_system_name == "") {
554         QCOMPARE(actual,expect);
555     }
556 #endif
557
558     delete canvas;
559 #endif
560 }
561
562 //the alignment tests may be trivial o.oa
563 void tst_qsgtext::horizontalAlignment()
564 {
565     //test one align each, and then test if two align fails.
566
567     for (int i = 0; i < standard.size(); i++)
568     {
569         for (int j=0; j < horizontalAlignmentmentStrings.size(); j++)
570         {
571             QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
572             QDeclarativeComponent textComponent(&engine);
573             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
574             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
575
576             QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
577
578             delete textObject;
579         }
580     }
581
582     for (int i = 0; i < richText.size(); i++)
583     {
584         for (int j=0; j < horizontalAlignmentmentStrings.size(); j++)
585         {
586             QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
587             QDeclarativeComponent textComponent(&engine);
588             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
589             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
590
591             QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
592
593             delete textObject;
594         }
595     }
596
597 }
598
599 void tst_qsgtext::horizontalAlignment_RightToLeft()
600 {
601     QSGView *canvas = createView(SRCDIR "/data/horizontalAlignment_RightToLeft.qml");
602     QSGText *text = canvas->rootObject()->findChild<QSGText*>("text");
603     QVERIFY(text != 0);
604     canvas->show();
605
606     QSGTextPrivate *textPrivate = QSGTextPrivate::get(text);
607     QVERIFY(textPrivate != 0);
608
609     // implicit alignment should follow the reading direction of RTL text
610     QCOMPARE(text->hAlign(), QSGText::AlignRight);
611     QCOMPARE(text->effectiveHAlign(), text->hAlign());
612     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
613
614     // explicitly left aligned text
615     text->setHAlign(QSGText::AlignLeft);
616     QCOMPARE(text->hAlign(), QSGText::AlignLeft);
617     QCOMPARE(text->effectiveHAlign(), text->hAlign());
618     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
619
620     // explicitly right aligned text
621     text->setHAlign(QSGText::AlignRight);
622     QCOMPARE(text->hAlign(), QSGText::AlignRight);
623     QCOMPARE(text->effectiveHAlign(), text->hAlign());
624     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
625
626     // change to rich text
627     QString textString = text->text();
628     text->setText(QString("<i>") + textString + QString("</i>"));
629     text->setTextFormat(QSGText::RichText);
630     text->resetHAlign();
631
632     // implicitly aligned rich text should follow the reading direction of text
633     QCOMPARE(text->hAlign(), QSGText::AlignRight);
634     QCOMPARE(text->effectiveHAlign(), text->hAlign());
635     QVERIFY(textPrivate->textDocument()->defaultTextOption().alignment() & Qt::AlignLeft);
636
637     // explicitly left aligned rich text
638     text->setHAlign(QSGText::AlignLeft);
639     QCOMPARE(text->hAlign(), QSGText::AlignLeft);
640     QCOMPARE(text->effectiveHAlign(), text->hAlign());
641     QVERIFY(textPrivate->textDocument()->defaultTextOption().alignment() & Qt::AlignRight);
642
643     // explicitly right aligned rich text
644     text->setHAlign(QSGText::AlignRight);
645     QCOMPARE(text->hAlign(), QSGText::AlignRight);
646     QCOMPARE(text->effectiveHAlign(), text->hAlign());
647     QVERIFY(textPrivate->textDocument()->defaultTextOption().alignment() & Qt::AlignLeft);
648
649     text->setText(textString);
650     text->setTextFormat(QSGText::PlainText);
651
652     // explicitly center aligned
653     text->setHAlign(QSGText::AlignHCenter);
654     QCOMPARE(text->hAlign(), QSGText::AlignHCenter);
655     QCOMPARE(text->effectiveHAlign(), text->hAlign());
656     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
657     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().right() > canvas->width()/2);
658
659     // reseted alignment should go back to following the text reading direction
660     text->resetHAlign();
661     QCOMPARE(text->hAlign(), QSGText::AlignRight);
662     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
663
664     // mirror the text item
665     QSGItemPrivate::get(text)->setLayoutMirror(true);
666
667     // mirrored implicit alignment should continue to follow the reading direction of the text
668     QCOMPARE(text->hAlign(), QSGText::AlignRight);
669     QCOMPARE(text->effectiveHAlign(), QSGText::AlignRight);
670     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
671
672     // mirrored explicitly right aligned behaves as left aligned
673     text->setHAlign(QSGText::AlignRight);
674     QCOMPARE(text->hAlign(), QSGText::AlignRight);
675     QCOMPARE(text->effectiveHAlign(), QSGText::AlignLeft);
676     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
677
678     // mirrored explicitly left aligned behaves as right aligned
679     text->setHAlign(QSGText::AlignLeft);
680     QCOMPARE(text->hAlign(), QSGText::AlignLeft);
681     QCOMPARE(text->effectiveHAlign(), QSGText::AlignRight);
682     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
683
684     // disable mirroring
685     QSGItemPrivate::get(text)->setLayoutMirror(false);
686     text->resetHAlign();
687
688     // English text should be implicitly left aligned
689     text->setText("Hello world!");
690     QCOMPARE(text->hAlign(), QSGText::AlignLeft);
691     QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
692
693 #ifndef Q_OS_MAC    // QTBUG-18040
694     // empty text with implicit alignment follows the system locale-based
695     // keyboard input direction from QApplication::keyboardInputDirection
696     text->setText("");
697     QCOMPARE(text->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
698                                   QSGText::AlignLeft : QSGText::AlignRight);
699     text->setHAlign(QSGText::AlignRight);
700     QCOMPARE(text->hAlign(), QSGText::AlignRight);
701 #endif
702
703     delete canvas;
704
705 #ifndef Q_OS_MAC    // QTBUG-18040
706     // alignment of Text with no text set to it
707     QString componentStr = "import QtQuick 2.0\nText {}";
708     QDeclarativeComponent textComponent(&engine);
709     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
710     QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
711     QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
712                                   QSGText::AlignLeft : QSGText::AlignRight);
713     delete textObject;
714 #endif
715 }
716
717 void tst_qsgtext::verticalAlignment()
718 {
719     //test one align each, and then test if two align fails.
720
721     for (int i = 0; i < standard.size(); i++)
722     {
723         for (int j=0; j < verticalAlignmentmentStrings.size(); j++)
724         {
725             QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
726             QDeclarativeComponent textComponent(&engine);
727             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
728             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
729
730             QVERIFY(textObject != 0);
731             QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
732
733             delete textObject;
734         }
735     }
736
737     for (int i = 0; i < richText.size(); i++)
738     {
739         for (int j=0; j < verticalAlignmentmentStrings.size(); j++)
740         {
741             QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
742             QDeclarativeComponent textComponent(&engine);
743             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
744             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
745
746             QVERIFY(textObject != 0);
747             QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
748
749             delete textObject;
750         }
751     }
752
753 }
754
755 void tst_qsgtext::font()
756 {
757     //test size, then bold, then italic, then family
758     {
759         QString componentStr = "import QtQuick 2.0\nText { font.pointSize: 40; text: \"Hello World\" }";
760         QDeclarativeComponent textComponent(&engine);
761         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
762         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
763
764         QCOMPARE(textObject->font().pointSize(), 40);
765         QCOMPARE(textObject->font().bold(), false);
766         QCOMPARE(textObject->font().italic(), false);
767
768         delete textObject;
769     }
770
771     {
772         QString componentStr = "import QtQuick 2.0\nText { font.pixelSize: 40; text: \"Hello World\" }";
773         QDeclarativeComponent textComponent(&engine);
774         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
775         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
776
777         QCOMPARE(textObject->font().pixelSize(), 40);
778         QCOMPARE(textObject->font().bold(), false);
779         QCOMPARE(textObject->font().italic(), false);
780
781         delete textObject;
782     }
783
784     { 
785         QString componentStr = "import QtQuick 2.0\nText { font.bold: true; text: \"Hello World\" }";
786         QDeclarativeComponent textComponent(&engine);
787         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
788         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
789
790         QCOMPARE(textObject->font().bold(), true);
791         QCOMPARE(textObject->font().italic(), false);
792
793         delete textObject;
794     }
795
796     { 
797         QString componentStr = "import QtQuick 2.0\nText { font.italic: true; text: \"Hello World\" }";
798         QDeclarativeComponent textComponent(&engine);
799         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
800         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
801
802         QCOMPARE(textObject->font().italic(), true);
803         QCOMPARE(textObject->font().bold(), false);
804
805         delete textObject;
806     }
807
808     { 
809         QString componentStr = "import QtQuick 2.0\nText { font.family: \"Helvetica\"; text: \"Hello World\" }";
810         QDeclarativeComponent textComponent(&engine);
811         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
812         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
813
814         QCOMPARE(textObject->font().family(), QString("Helvetica"));
815         QCOMPARE(textObject->font().bold(), false);
816         QCOMPARE(textObject->font().italic(), false);
817
818         delete textObject;
819     }
820
821     { 
822         QString componentStr = "import QtQuick 2.0\nText { font.family: \"\"; text: \"Hello World\" }";
823         QDeclarativeComponent textComponent(&engine);
824         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
825         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
826
827         QCOMPARE(textObject->font().family(), QString(""));
828
829         delete textObject;
830     }
831 }
832
833 void tst_qsgtext::style()
834 {
835     //test style
836     for (int i = 0; i < styles.size(); i++)
837     { 
838         QString componentStr = "import QtQuick 2.0\nText { style: \"" + styleStrings.at(i) + "\"; styleColor: \"white\"; text: \"Hello World\" }";
839         QDeclarativeComponent textComponent(&engine);
840         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
841         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
842
843         QCOMPARE((int)textObject->style(), (int)styles.at(i));
844         QCOMPARE(textObject->styleColor(), QColor("white"));
845
846         delete textObject;
847     }
848     QString componentStr = "import QtQuick 2.0\nText { text: \"Hello World\" }";
849     QDeclarativeComponent textComponent(&engine);
850     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
851     QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
852
853     QRectF brPre = textObject->boundingRect();
854     textObject->setStyle(QSGText::Outline);
855     QRectF brPost = textObject->boundingRect();
856
857     QVERIFY(brPre.width() < brPost.width());
858     QVERIFY(brPre.height() < brPost.height());
859
860     delete textObject;
861 }
862
863 void tst_qsgtext::color()
864 {
865     //test style
866     for (int i = 0; i < colorStrings.size(); i++)
867     { 
868         QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
869         QDeclarativeComponent textComponent(&engine);
870         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
871         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
872
873         QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
874         QCOMPARE(textObject->styleColor(), QColor());
875
876         delete textObject;
877     }
878
879     for (int i = 0; i < colorStrings.size(); i++)
880     { 
881         QString componentStr = "import QtQuick 2.0\nText { styleColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
882         QDeclarativeComponent textComponent(&engine);
883         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
884         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
885
886         QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(i)));
887         // default color to black?
888         QCOMPARE(textObject->color(), QColor("black"));
889
890         delete textObject;
891     }
892     
893     for (int i = 0; i < colorStrings.size(); i++)
894     { 
895         for (int j = 0; j < colorStrings.size(); j++)
896         {
897             QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStrings.at(i) + "\"; styleColor: \"" + colorStrings.at(j) + "\"; text: \"Hello World\" }";
898             QDeclarativeComponent textComponent(&engine);
899             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
900             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
901
902             QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
903             QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(j)));
904
905             delete textObject;
906         }
907     }
908     {
909         QString colorStr = "#AA001234";
910         QColor testColor("#001234");
911         testColor.setAlpha(170);
912
913         QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStr + "\"; text: \"Hello World\" }";
914         QDeclarativeComponent textComponent(&engine);
915         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
916         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
917
918         QCOMPARE(textObject->color(), testColor);
919
920         delete textObject;
921     }
922 }
923
924 void tst_qsgtext::smooth()
925 {
926     for (int i = 0; i < standard.size(); i++)
927     {
928         {
929             QString componentStr = "import QtQuick 2.0\nText { smooth: true; text: \"" + standard.at(i) + "\" }";
930             QDeclarativeComponent textComponent(&engine);
931             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
932             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
933             QCOMPARE(textObject->smooth(), true);
934
935             delete textObject;
936         }
937         {
938             QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
939             QDeclarativeComponent textComponent(&engine);
940             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
941             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
942             QCOMPARE(textObject->smooth(), false);
943
944             delete textObject;
945         }
946     }
947     for (int i = 0; i < richText.size(); i++)
948     {
949         {
950             QString componentStr = "import QtQuick 2.0\nText { smooth: true; text: \"" + richText.at(i) + "\" }";
951             QDeclarativeComponent textComponent(&engine);
952             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
953             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
954             QCOMPARE(textObject->smooth(), true);
955
956             delete textObject;
957         }
958         {
959             QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
960             QDeclarativeComponent textComponent(&engine);
961             textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
962             QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
963             QCOMPARE(textObject->smooth(), false);
964
965             delete textObject;
966         }
967     }
968 }
969
970 void tst_qsgtext::weight()
971 {
972     {
973         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
974         QDeclarativeComponent textComponent(&engine);
975         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
976         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
977
978         QVERIFY(textObject != 0);
979         QCOMPARE((int)textObject->font().weight(), (int)QDeclarativeFontValueType::Normal);
980
981         delete textObject;
982     }
983     {
984         QString componentStr = "import QtQuick 2.0\nText { font.weight: \"Bold\"; text: \"Hello world!\" }";
985         QDeclarativeComponent textComponent(&engine);
986         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
987         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
988
989         QVERIFY(textObject != 0);
990         QCOMPARE((int)textObject->font().weight(), (int)QDeclarativeFontValueType::Bold);
991
992         delete textObject;
993     }
994 }
995
996 void tst_qsgtext::underline()
997 {
998     {
999         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1000         QDeclarativeComponent textComponent(&engine);
1001         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1002         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1003
1004         QVERIFY(textObject != 0);
1005         QCOMPARE(textObject->font().underline(), false);
1006
1007         delete textObject;
1008     }
1009     {
1010         QString componentStr = "import QtQuick 2.0\nText { font.underline: true; text: \"Hello world!\" }";
1011         QDeclarativeComponent textComponent(&engine);
1012         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1013         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1014
1015         QVERIFY(textObject != 0);
1016         QCOMPARE(textObject->font().underline(), true);
1017
1018         delete textObject;
1019     }
1020 }
1021
1022 void tst_qsgtext::overline()
1023 {
1024     {
1025         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1026         QDeclarativeComponent textComponent(&engine);
1027         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1028         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1029
1030         QVERIFY(textObject != 0);
1031         QCOMPARE(textObject->font().overline(), false);
1032
1033         delete textObject;
1034     }
1035     {
1036         QString componentStr = "import QtQuick 2.0\nText { font.overline: true; text: \"Hello world!\" }";
1037         QDeclarativeComponent textComponent(&engine);
1038         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1039         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1040
1041         QVERIFY(textObject != 0);
1042         QCOMPARE(textObject->font().overline(), true);
1043
1044         delete textObject;
1045     }
1046 }
1047
1048 void tst_qsgtext::strikeout()
1049 {
1050     {
1051         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1052         QDeclarativeComponent textComponent(&engine);
1053         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1054         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1055
1056         QVERIFY(textObject != 0);
1057         QCOMPARE(textObject->font().strikeOut(), false);
1058
1059         delete textObject;
1060     }
1061     {
1062         QString componentStr = "import QtQuick 2.0\nText { font.strikeout: true; text: \"Hello world!\" }";
1063         QDeclarativeComponent textComponent(&engine);
1064         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1065         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1066
1067         QVERIFY(textObject != 0);
1068         QCOMPARE(textObject->font().strikeOut(), true);
1069
1070         delete textObject;
1071     }
1072 }
1073
1074 void tst_qsgtext::capitalization()
1075 {
1076     {
1077         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1078         QDeclarativeComponent textComponent(&engine);
1079         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1080         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1081
1082         QVERIFY(textObject != 0);
1083         QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::MixedCase);
1084
1085         delete textObject;
1086     }
1087     {
1088         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllUppercase\" }";
1089         QDeclarativeComponent textComponent(&engine);
1090         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1091         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1092
1093         QVERIFY(textObject != 0);
1094         QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::AllUppercase);
1095
1096         delete textObject;
1097     }
1098     {
1099         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllLowercase\" }";
1100         QDeclarativeComponent textComponent(&engine);
1101         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1102         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1103
1104         QVERIFY(textObject != 0);
1105         QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::AllLowercase);
1106
1107         delete textObject;
1108     }
1109     {
1110         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"SmallCaps\" }";
1111         QDeclarativeComponent textComponent(&engine);
1112         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1113         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1114
1115         QVERIFY(textObject != 0);
1116         QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::SmallCaps);
1117
1118         delete textObject;
1119     }
1120     {
1121         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"Capitalize\" }";
1122         QDeclarativeComponent textComponent(&engine);
1123         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1124         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1125
1126         QVERIFY(textObject != 0);
1127         QCOMPARE((int)textObject->font().capitalization(), (int)QDeclarativeFontValueType::Capitalize);
1128
1129         delete textObject;
1130     }
1131 }
1132
1133 void tst_qsgtext::letterSpacing()
1134 {
1135     {
1136         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1137         QDeclarativeComponent textComponent(&engine);
1138         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1139         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1140
1141         QVERIFY(textObject != 0);
1142         QCOMPARE(textObject->font().letterSpacing(), 0.0);
1143
1144         delete textObject;
1145     }
1146     {
1147         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: -2 }";
1148         QDeclarativeComponent textComponent(&engine);
1149         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1150         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1151
1152         QVERIFY(textObject != 0);
1153         QCOMPARE(textObject->font().letterSpacing(), -2.);
1154
1155         delete textObject;
1156     }
1157     {
1158         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: 3 }";
1159         QDeclarativeComponent textComponent(&engine);
1160         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1161         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1162
1163         QVERIFY(textObject != 0);
1164         QCOMPARE(textObject->font().letterSpacing(), 3.);
1165
1166         delete textObject;
1167     }
1168 }
1169
1170 void tst_qsgtext::wordSpacing()
1171 {
1172     {
1173         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
1174         QDeclarativeComponent textComponent(&engine);
1175         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1176         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1177
1178         QVERIFY(textObject != 0);
1179         QCOMPARE(textObject->font().wordSpacing(), 0.0);
1180
1181         delete textObject;
1182     }
1183     {
1184         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: -50 }";
1185         QDeclarativeComponent textComponent(&engine);
1186         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1187         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1188
1189         QVERIFY(textObject != 0);
1190         QCOMPARE(textObject->font().wordSpacing(), -50.);
1191
1192         delete textObject;
1193     }
1194     {
1195         QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: 200 }";
1196         QDeclarativeComponent textComponent(&engine);
1197         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1198         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1199
1200         QVERIFY(textObject != 0);
1201         QCOMPARE(textObject->font().wordSpacing(), 200.);
1202
1203         delete textObject;
1204     }
1205 }
1206
1207
1208
1209
1210 class EventSender : public QSGItem
1211 {
1212 public:
1213     void sendEvent(QMouseEvent *event) {
1214         if (event->type() == QEvent::MouseButtonPress)
1215             mousePressEvent(event);
1216         else if (event->type() == QEvent::MouseButtonRelease)
1217             mouseReleaseEvent(event);
1218         else
1219             qWarning() << "Trying to send unsupported event type";
1220     }
1221 };
1222
1223 class LinkTest : public QObject
1224 {
1225     Q_OBJECT
1226 public:
1227     LinkTest() {}
1228
1229     QString link;
1230
1231 public slots:
1232     void linkClicked(QString l) { link = l; }
1233 };
1234
1235 void tst_qsgtext::clickLink()
1236 {
1237     {
1238         QString componentStr = "import QtQuick 2.0\nText { text: \"<a href=\\\"http://qt.nokia.com\\\">Hello world!</a>\" }";
1239         QDeclarativeComponent textComponent(&engine);
1240         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1241         QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1242
1243         QVERIFY(textObject != 0);
1244
1245         LinkTest test;
1246         QObject::connect(textObject, SIGNAL(linkActivated(QString)), &test, SLOT(linkClicked(QString)));
1247
1248         {
1249             QMouseEvent me(QEvent::MouseButtonPress,QPointF(textObject->x()/2, textObject->y()/2), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1250             static_cast<EventSender*>(static_cast<QSGItem*>(textObject))->sendEvent(&me);
1251
1252         }
1253
1254         {
1255             QMouseEvent me(QEvent::MouseButtonRelease,QPointF(textObject->x()/2, textObject->y()/2), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1256             static_cast<EventSender*>(static_cast<QSGItem*>(textObject))->sendEvent(&me);
1257
1258         }
1259
1260
1261         QCOMPARE(test.link, QLatin1String("http://qt.nokia.com"));
1262
1263         delete textObject;
1264     }
1265 }
1266
1267 void tst_qsgtext::embeddedImages_data()
1268 {
1269     QTest::addColumn<QUrl>("qmlfile");
1270     QTest::addColumn<QString>("error");
1271     QTest::newRow("local") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesLocal.qml") << "";
1272     QTest::newRow("local-error") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesLocalError.qml")
1273         << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesLocalError.qml").toString()+":3:1: QML Text: Cannot open: " + QUrl::fromLocalFile(SRCDIR "/data/http/notexists.png").toString();
1274     QTest::newRow("remote") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesRemote.qml") << "";
1275     QTest::newRow("remote-error") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesRemoteError.qml")
1276         << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error downloading http://127.0.0.1:14453/notexists.png - server replied: Not found";
1277 }
1278
1279 void tst_qsgtext::embeddedImages()
1280 {
1281     // Tests QTBUG-9900
1282
1283     QFETCH(QUrl, qmlfile);
1284     QFETCH(QString, error);
1285
1286     TestHTTPServer server(14453);
1287     server.serveDirectory(SRCDIR "/data/http");
1288
1289     if (!error.isEmpty())
1290         QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
1291     
1292     QDeclarativeComponent textComponent(&engine, qmlfile);
1293     QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1294
1295     QVERIFY(textObject != 0);
1296
1297     QTRY_COMPARE(textObject->resourcesLoading(), 0);
1298
1299     QPixmap pm(SRCDIR "/data/http/exists.png");
1300     if (error.isEmpty()) {
1301         QCOMPARE(textObject->width(), double(pm.width()));
1302         QCOMPARE(textObject->height(), double(pm.height()));
1303     } else {
1304         QVERIFY(16 != pm.width()); // check test is effective
1305         QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
1306         QCOMPARE(textObject->height(), 16.0);
1307     }
1308
1309     delete textObject;
1310 }
1311
1312 void tst_qsgtext::lineCount()
1313 {
1314     QSGView *canvas = createView(SRCDIR "/data/lineCount.qml");
1315
1316     QSGText *myText = canvas->rootObject()->findChild<QSGText*>("myText");
1317     QVERIFY(myText != 0);
1318
1319     QVERIFY(myText->lineCount() > 1);
1320     QVERIFY(!myText->truncated());
1321     QCOMPARE(myText->maximumLineCount(), INT_MAX);
1322
1323     myText->setMaximumLineCount(2);
1324     QCOMPARE(myText->lineCount(), 2);
1325     QCOMPARE(myText->truncated(), true);
1326     QCOMPARE(myText->maximumLineCount(), 2);
1327
1328     myText->resetMaximumLineCount();
1329     QCOMPARE(myText->maximumLineCount(), INT_MAX);
1330     QCOMPARE(myText->truncated(), false);
1331
1332     myText->setElideMode(QSGText::ElideRight);
1333     myText->setMaximumLineCount(2);
1334     QCOMPARE(myText->lineCount(), 2);
1335     QCOMPARE(myText->truncated(), true);
1336     QCOMPARE(myText->maximumLineCount(), 2);
1337
1338     delete canvas;
1339 }
1340
1341 void tst_qsgtext::lineHeight()
1342 {
1343     QSGView *canvas = createView(SRCDIR "/data/lineHeight.qml");
1344
1345     QSGText *myText = canvas->rootObject()->findChild<QSGText*>("myText");
1346     QVERIFY(myText != 0);
1347
1348     QVERIFY(myText->lineHeight() == 1);
1349     QVERIFY(myText->lineHeightMode() == QSGText::ProportionalHeight);
1350
1351     qreal h = myText->height();
1352     myText->setLineHeight(1.5);
1353 #ifdef Q_WS_QPA
1354     QEXPECT_FAIL("", "QTBUG-21009 fails", Continue);
1355 #endif
1356     QVERIFY(myText->height() == h * 1.5);
1357
1358     myText->setLineHeightMode(QSGText::FixedHeight);
1359     myText->setLineHeight(20);
1360     QCOMPARE(myText->height(), myText->lineCount() * 20.0);
1361
1362     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.");
1363     myText->setLineHeightMode(QSGText::ProportionalHeight);
1364     myText->setLineHeight(1.0);
1365
1366     qreal h2 = myText->height();
1367     myText->setLineHeight(2.0);
1368     QVERIFY(myText->height() == h2 * 2.0);
1369
1370     myText->setLineHeightMode(QSGText::FixedHeight);
1371     myText->setLineHeight(10);
1372     QCOMPARE(myText->height(), myText->lineCount() * 10.0);
1373
1374     delete canvas;
1375 }
1376
1377 void tst_qsgtext::implicitSize_data()
1378 {
1379     QTest::addColumn<QString>("text");
1380     QTest::addColumn<QString>("wrap");
1381     QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "Text.NoWrap";
1382     QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "Text.NoWrap";
1383     QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "Text.Wrap";
1384     QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "Text.Wrap";
1385 }
1386
1387 void tst_qsgtext::implicitSize()
1388 {
1389     QFETCH(QString, text);
1390     QFETCH(QString, wrap);
1391     QString componentStr = "import QtQuick 2.0\nText { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
1392     QDeclarativeComponent textComponent(&engine);
1393     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1394     QSGText *textObject = qobject_cast<QSGText*>(textComponent.create());
1395
1396     QVERIFY(textObject->width() < textObject->implicitWidth());
1397     QVERIFY(textObject->height() == textObject->implicitHeight());
1398
1399     textObject->resetWidth();
1400     QVERIFY(textObject->width() == textObject->implicitWidth());
1401     QVERIFY(textObject->height() == textObject->implicitHeight());
1402
1403     delete textObject;
1404 }
1405
1406 void tst_qsgtext::lineLaidOut()
1407 {
1408     QSGView *canvas = createView(SRCDIR "/data/lineLayout.qml");
1409
1410     QSGText *myText = canvas->rootObject()->findChild<QSGText*>("myText");
1411     QVERIFY(myText != 0);
1412
1413     QSGTextPrivate *textPrivate = QSGTextPrivate::get(myText);
1414     QVERIFY(textPrivate != 0);
1415
1416     QTextDocument *doc = textPrivate->textDocument();
1417     QVERIFY(doc == 0);
1418
1419     QVERIFY(myText->lineCount() == textPrivate->linesRects.count());
1420
1421     for (int i = 0; i < textPrivate->linesRects.count(); ++i) {
1422         QRectF r = textPrivate->linesRects.at(i);
1423         QVERIFY(r.width() == i * 15);
1424         if (i >= 30)
1425             QVERIFY(r.x() == r.width() + 30);
1426         if (i >= 60) {
1427             QVERIFY(r.x() == r.width() * 2 + 60);
1428             QVERIFY(r.height() == 20);
1429         }
1430     }
1431 }
1432
1433 QTEST_MAIN(tst_qsgtext)
1434
1435 #include "tst_qsgtext.moc"