#include <QtQml/qqmlcomponent.h>
#include <QtQuick/private/qquicktext_p.h>
#include <private/qquicktext_p_p.h>
-#include <private/qqmlvaluetype_p.h>
+#include <private/qquickvaluetypes_p.h>
#include <QFontMetrics>
-#include <QGraphicsSceneMouseEvent>
#include <qmath.h>
#include <QtQuick/QQuickView>
-#include <private/qapplication_p.h>
+#include <private/qguiapplication_p.h>
#include <limits.h>
#include <QtGui/QMouseEvent>
#include "../../shared/util.h"
DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
+#define SERVER_PORT 14459
+#define SERVER_ADDR "http://127.0.0.1:14459"
+
Q_DECLARE_METATYPE(QQuickText::TextFormat)
+QT_BEGIN_NAMESPACE
+extern void qt_setQtEnableTestFont(bool value);
+QT_END_NAMESPACE
+
class tst_qquicktext : public QQmlDataTest
{
Q_OBJECT
void elide();
void multilineElide_data();
void multilineElide();
+ void implicitElide_data();
+ void implicitElide();
void textFormat();
- void alignments_data();
- void alignments();
-
void baseUrl();
void embeddedImages_data();
void embeddedImages();
void horizontalAlignment();
void horizontalAlignment_RightToLeft();
void verticalAlignment();
+ void hAlignImplicitWidth();
void font();
void style();
void color();
void smooth();
+ void renderType();
- // QQmlFontValueType
+ // QQuickFontValueType
void weight();
void underline();
void overline();
void letterSpacing();
void wordSpacing();
+ void clickLink_data();
void clickLink();
void implicitSize_data();
void implicitSize();
void contentSize();
+ void implicitSizeBinding_data();
+ void implicitSizeBinding();
+ void geometryChanged();
+ void boundingRect_data();
+ void boundingRect();
+ void clipRect();
void lineLaidOut();
+ void lineLaidOutRelayout();
void imgTagsBaseUrl_data();
void imgTagsBaseUrl();
void fontFormatSizes_data();
void fontFormatSizes();
+ void baselineOffset_data();
+ void baselineOffset();
+
+ void htmlLists();
+ void htmlLists_data();
+
private:
QStringList standard;
QStringList richText;
QQmlEngine engine;
QQuickView *createView(const QString &filename);
+ int numberOfNonWhitePixels(int fromX, int toX, const QImage &image);
};
tst_qquicktext::tst_qquicktext()
// << "#AA0011DD"
// << "#00F16B11";
//
+ qt_setQtEnableTestFont(true);
}
QQuickView *tst_qquicktext::createView(const QString &filename)
{
- QQuickView *canvas = new QQuickView(0);
+ QQuickView *window = new QQuickView(0);
- canvas->setSource(QUrl::fromLocalFile(filename));
- return canvas;
+ window->setSource(QUrl::fromLocalFile(filename));
+ return window;
}
void tst_qquicktext::text()
delete textObject;
}
+
+ {
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0\n Text {}", QUrl());
+ QScopedPointer<QObject> object(component.create());
+ QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
+ QVERIFY(textObject);
+
+ QSignalSpy spy(textObject, SIGNAL(wrapModeChanged()));
+
+ QCOMPARE(textObject->wrapMode(), QQuickText::NoWrap);
+
+ textObject->setWrapMode(QQuickText::Wrap);
+ QCOMPARE(textObject->wrapMode(), QQuickText::Wrap);
+ QCOMPARE(spy.count(), 1);
+
+ textObject->setWrapMode(QQuickText::Wrap);
+ QCOMPARE(spy.count(), 1);
+
+ textObject->setWrapMode(QQuickText::NoWrap);
+ QCOMPARE(textObject->wrapMode(), QQuickText::NoWrap);
+ QCOMPARE(spy.count(), 2);
+ }
}
void tst_qquicktext::elide()
void tst_qquicktext::multilineElide()
{
QFETCH(QQuickText::TextFormat, format);
- QQuickView *canvas = createView(testFile("multilineelide.qml"));
+ QQuickView *window = createView(testFile("multilineelide.qml"));
- QQuickText *myText = qobject_cast<QQuickText*>(canvas->rootObject());
+ QQuickText *myText = qobject_cast<QQuickText*>(window->rootObject());
QVERIFY(myText != 0);
myText->setTextFormat(format);
myText->setLineHeight(1.1);
QCOMPARE(myText->lineCount(), 1);
- delete canvas;
+ delete window;
+}
+
+void tst_qquicktext::implicitElide_data()
+{
+ QTest::addColumn<QString>("width");
+ QTest::addColumn<QString>("initialText");
+ QTest::addColumn<QString>("text");
+
+ QTest::newRow("maximum width, empty")
+ << "Math.min(implicitWidth, 100)"
+ << "";
+ QTest::newRow("maximum width, short")
+ << "Math.min(implicitWidth, 100)"
+ << "the";
+ QTest::newRow("maximum width, long")
+ << "Math.min(implicitWidth, 100)"
+ << "the quick brown fox jumped over the lazy dog";
+ QTest::newRow("reset width, empty")
+ << "implicitWidth > 100 ? 100 : undefined"
+ << "";
+ QTest::newRow("reset width, short")
+ << "implicitWidth > 100 ? 100 : undefined"
+ << "the";
+ QTest::newRow("reset width, long")
+ << "implicitWidth > 100 ? 100 : undefined"
+ << "the quick brown fox jumped over the lazy dog";
+}
+
+void tst_qquicktext::implicitElide()
+{
+ QFETCH(QString, width);
+ QFETCH(QString, initialText);
+
+ QString componentStr =
+ "import QtQuick 2.0\n"
+ "Text {\n"
+ "width: " + width + "\n"
+ "text: \"" + initialText + "\"\n"
+ "elide: Text.ElideRight\n"
+ "}";
+ QQmlComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+
+ QVERIFY(textObject->contentWidth() <= textObject->width());
+
+ textObject->setText("the quick brown fox jumped over");
+
+ QVERIFY(textObject->contentWidth() > 0);
+ QVERIFY(textObject->contentWidth() <= textObject->width());
}
void tst_qquicktext::textFormat()
delete textObject;
}
-}
-
-void tst_qquicktext::alignments_data()
-{
- QTest::addColumn<int>("hAlign");
- QTest::addColumn<int>("vAlign");
- QTest::addColumn<QString>("expectfile");
-
- QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << testFile("alignments_lt.png");
- QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << testFile("alignments_rt.png");
- QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << testFile("alignments_ct.png");
-
- QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << testFile("alignments_lb.png");
- QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << testFile("alignments_rb.png");
- QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << testFile("alignments_cb.png");
+ {
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0\n Text {}", QUrl());
+ QScopedPointer<QObject> object(component.create());
+ QQuickText *text = qobject_cast<QQuickText *>(object.data());
+ QVERIFY(text);
- QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << testFile("alignments_lc.png");
- QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << testFile("alignments_rc.png");
- QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << testFile("alignments_cc.png");
-}
+ QSignalSpy spy(text, SIGNAL(textFormatChanged(TextFormat)));
+ QCOMPARE(text->textFormat(), QQuickText::AutoText);
-void tst_qquicktext::alignments()
-{
- QSKIP("Text alignment pixmap comparison tests will not work with scenegraph");
-#if (0)// No widgets in scenegraph
- QFETCH(int, hAlign);
- QFETCH(int, vAlign);
- QFETCH(QString, expectfile);
-
- QQuickView *canvas = createView(testFile("alignments.qml"));
- canvas->show();
- canvas->requestActivateWindow();
- QTest::qWait(50);
- QTRY_COMPARE(QGuiApplication::activeWindow(), static_cast<QWidget *>(canvas));
+ text->setTextFormat(QQuickText::StyledText);
+ QCOMPARE(text->textFormat(), QQuickText::StyledText);
+ QCOMPARE(spy.count(), 1);
- QObject *ob = canvas->rootObject();
- QVERIFY(ob != 0);
- ob->setProperty("horizontalAlignment",hAlign);
- ob->setProperty("verticalAlignment",vAlign);
- QTRY_COMPARE(ob->property("running").toBool(),false);
- QImage actual(canvas->width(), canvas->height(), QImage::Format_RGB32);
- actual.fill(qRgb(255,255,255));
- QPainter p(&actual);
- canvas->render(&p);
+ text->setTextFormat(QQuickText::StyledText);
+ QCOMPARE(spy.count(), 1);
- QImage expect(expectfile);
- if (QGuiApplicationPrivate::graphics_system_name == "raster" || QGuiApplicationPrivate::graphics_system_name == "") {
- QCOMPARE(actual,expect);
+ text->setTextFormat(QQuickText::AutoText);
+ QCOMPARE(text->textFormat(), QQuickText::AutoText);
+ QCOMPARE(spy.count(), 2);
}
- delete canvas;
-#endif
}
//the alignment tests may be trivial o.oa
void tst_qquicktext::horizontalAlignment_RightToLeft()
{
- QQuickView *canvas = createView(testFile("horizontalAlignment_RightToLeft.qml"));
- QQuickText *text = canvas->rootObject()->findChild<QQuickText*>("text");
+ QQuickView *window = createView(testFile("horizontalAlignment_RightToLeft.qml"));
+ QQuickText *text = window->rootObject()->findChild<QQuickText*>("text");
QVERIFY(text != 0);
- canvas->show();
+ window->show();
QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(text);
QVERIFY(textPrivate != 0);
// implicit alignment should follow the reading direction of RTL text
QCOMPARE(text->hAlign(), QQuickText::AlignRight);
QCOMPARE(text->effectiveHAlign(), text->hAlign());
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
// explicitly left aligned text
text->setHAlign(QQuickText::AlignLeft);
QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
QCOMPARE(text->effectiveHAlign(), text->hAlign());
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
// explicitly right aligned text
text->setHAlign(QQuickText::AlignRight);
QCOMPARE(text->hAlign(), QQuickText::AlignRight);
QCOMPARE(text->effectiveHAlign(), text->hAlign());
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
// change to rich text
QString textString = text->text();
text->setHAlign(QQuickText::AlignHCenter);
QCOMPARE(text->hAlign(), QQuickText::AlignHCenter);
QCOMPARE(text->effectiveHAlign(), text->hAlign());
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().right() > canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().right() > window->width()/2);
// reseted alignment should go back to following the text reading direction
text->resetHAlign();
QCOMPARE(text->hAlign(), QQuickText::AlignRight);
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
// mirror the text item
QQuickItemPrivate::get(text)->setLayoutMirror(true);
// mirrored implicit alignment should continue to follow the reading direction of the text
QCOMPARE(text->hAlign(), QQuickText::AlignRight);
QCOMPARE(text->effectiveHAlign(), QQuickText::AlignRight);
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
// mirrored explicitly right aligned behaves as left aligned
text->setHAlign(QQuickText::AlignRight);
QCOMPARE(text->hAlign(), QQuickText::AlignRight);
QCOMPARE(text->effectiveHAlign(), QQuickText::AlignLeft);
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
// mirrored explicitly left aligned behaves as right aligned
text->setHAlign(QQuickText::AlignLeft);
QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
QCOMPARE(text->effectiveHAlign(), QQuickText::AlignRight);
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
// disable mirroring
QQuickItemPrivate::get(text)->setLayoutMirror(false);
// English text should be implicitly left aligned
text->setText("Hello world!");
QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
- QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+ QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
// empty text with implicit alignment follows the system locale-based
// keyboard input direction from QInputMethod::inputDirection()
text->setHAlign(QQuickText::AlignRight);
QCOMPARE(text->hAlign(), QQuickText::AlignRight);
- delete canvas;
+ delete window;
// alignment of Text with no text set to it
QString componentStr = "import QtQuick 2.0\nText {}";
delete textObject;
}
+int tst_qquicktext::numberOfNonWhitePixels(int fromX, int toX, const QImage &image)
+{
+ int pixels = 0;
+ for (int x = fromX; x < toX; ++x) {
+ for (int y = 0; y < image.height(); ++y) {
+ if (image.pixel(x, y) != qRgb(255, 255, 255))
+ pixels++;
+ }
+ }
+ return pixels;
+}
+
+void tst_qquicktext::hAlignImplicitWidth()
+{
+ QQuickView view(testFileUrl("hAlignImplicitWidth.qml"));
+ view.show();
+ view.requestActivateWindow();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQuickText *text = view.rootObject()->findChild<QQuickText*>("textItem");
+ QVERIFY(text != 0);
+
+ {
+ // Left Align
+ QImage image = view.grabWindow();
+ int left = numberOfNonWhitePixels(0, image.width() / 3, image);
+ int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
+ int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
+ QVERIFY(left > mid);
+ QVERIFY(mid > right);
+ }
+ {
+ // HCenter Align
+ text->setHAlign(QQuickText::AlignHCenter);
+ QImage image = view.grabWindow();
+ int left = numberOfNonWhitePixels(0, image.width() / 3, image);
+ int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
+ int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
+ QVERIFY(left < mid);
+ QVERIFY(mid > right);
+ }
+ {
+ // Right Align
+ text->setHAlign(QQuickText::AlignRight);
+ QImage image = view.grabWindow();
+ int left = numberOfNonWhitePixels(0, image.width() / 3, image);
+ int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
+ int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
+ QVERIFY(left < mid);
+ QVERIFY(mid < right);
+ }
+}
+
void tst_qquicktext::verticalAlignment()
{
//test one align each, and then test if two align fails.
QCOMPARE(textObject->color(), QColor("black"));
QCOMPARE(textObject->linkColor(), QColor("blue"));
+ QSignalSpy colorSpy(textObject, SIGNAL(colorChanged()));
+ QSignalSpy linkColorSpy(textObject, SIGNAL(linkColorChanged()));
+
+ textObject->setColor(QColor("white"));
+ QCOMPARE(textObject->color(), QColor("white"));
+ QCOMPARE(colorSpy.count(), 1);
+
+ textObject->setLinkColor(QColor("black"));
+ QCOMPARE(textObject->linkColor(), QColor("black"));
+ QCOMPARE(linkColorSpy.count(), 1);
+
+ textObject->setColor(QColor("white"));
+ QCOMPARE(colorSpy.count(), 1);
+
+ textObject->setLinkColor(QColor("black"));
+ QCOMPARE(linkColorSpy.count(), 1);
+
+ textObject->setColor(QColor("black"));
+ QCOMPARE(textObject->color(), QColor("black"));
+ QCOMPARE(colorSpy.count(), 2);
+
+ textObject->setLinkColor(QColor("blue"));
+ QCOMPARE(textObject->linkColor(), QColor("blue"));
+ QCOMPARE(linkColorSpy.count(), 2);
+
delete textObject;
}
for (int i = 0; i < standard.size(); i++)
{
{
- QString componentStr = "import QtQuick 2.0\nText { smooth: true; text: \"" + standard.at(i) + "\" }";
+ QString componentStr = "import QtQuick 2.0\nText { smooth: false; text: \"" + standard.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
- QCOMPARE(textObject->smooth(), true);
+ QCOMPARE(textObject->smooth(), false);
delete textObject;
}
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
- QCOMPARE(textObject->smooth(), false);
+ QCOMPARE(textObject->smooth(), true);
delete textObject;
}
for (int i = 0; i < richText.size(); i++)
{
{
- QString componentStr = "import QtQuick 2.0\nText { smooth: true; text: \"" + richText.at(i) + "\" }";
+ QString componentStr = "import QtQuick 2.0\nText { smooth: false; text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
- QCOMPARE(textObject->smooth(), true);
+ QCOMPARE(textObject->smooth(), false);
delete textObject;
}
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
- QCOMPARE(textObject->smooth(), false);
+ QCOMPARE(textObject->smooth(), true);
delete textObject;
}
}
}
-void tst_qquicktext::weight()
+void tst_qquicktext::renderType()
{
- {
- QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
- QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0\n Text {}", QUrl());
+ QScopedPointer<QObject> object(component.create());
+ QQuickText *text = qobject_cast<QQuickText *>(object.data());
+ QVERIFY(text);
- QVERIFY(textObject != 0);
- QCOMPARE((int)textObject->font().weight(), (int)QQmlFontValueType::Normal);
+ QSignalSpy spy(text, SIGNAL(renderTypeChanged()));
- delete textObject;
- }
- {
- QString componentStr = "import QtQuick 2.0\nText { font.weight: \"Bold\"; text: \"Hello world!\" }";
- QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QCOMPARE(text->renderType(), QQuickText::QtRendering);
- QVERIFY(textObject != 0);
- QCOMPARE((int)textObject->font().weight(), (int)QQmlFontValueType::Bold);
+ text->setRenderType(QQuickText::NativeRendering);
+ QCOMPARE(text->renderType(), QQuickText::NativeRendering);
+ QCOMPARE(spy.count(), 1);
- delete textObject;
- }
+ text->setRenderType(QQuickText::NativeRendering);
+ QCOMPARE(spy.count(), 1);
+
+ text->setRenderType(QQuickText::QtRendering);
+ QCOMPARE(text->renderType(), QQuickText::QtRendering);
+ QCOMPARE(spy.count(), 2);
}
-void tst_qquicktext::underline()
+void tst_qquicktext::weight()
{
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != 0);
- QCOMPARE(textObject->font().underline(), false);
+ QCOMPARE((int)textObject->font().weight(), (int)QQuickFontValueType::Normal);
delete textObject;
}
{
- QString componentStr = "import QtQuick 2.0\nText { font.underline: true; text: \"Hello world!\" }";
+ QString componentStr = "import QtQuick 2.0\nText { font.weight: \"Bold\"; text: \"Hello world!\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != 0);
- QCOMPARE(textObject->font().underline(), true);
+ QCOMPARE((int)textObject->font().weight(), (int)QQuickFontValueType::Bold);
delete textObject;
}
}
-void tst_qquicktext::overline()
+void tst_qquicktext::underline()
{
- {
- QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
- QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
- QVERIFY(textObject != 0);
- QCOMPARE(textObject->font().overline(), false);
-
- delete textObject;
- }
- {
- QString componentStr = "import QtQuick 2.0\nText { font.overline: true; text: \"Hello world!\" }";
- QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
- QVERIFY(textObject != 0);
- QCOMPARE(textObject->font().overline(), true);
+ QQuickView view(testFileUrl("underline.qml"));
+ view.show();
+ view.requestActivateWindow();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+ QQuickText *textObject = view.rootObject()->findChild<QQuickText*>("myText");
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().overline(), false);
+ QCOMPARE(textObject->font().underline(), true);
+ QCOMPARE(textObject->font().strikeOut(), false);
+}
- delete textObject;
- }
+void tst_qquicktext::overline()
+{
+ QQuickView view(testFileUrl("overline.qml"));
+ view.show();
+ view.requestActivateWindow();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+ QQuickText *textObject = view.rootObject()->findChild<QQuickText*>("myText");
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().overline(), true);
+ QCOMPARE(textObject->font().underline(), false);
+ QCOMPARE(textObject->font().strikeOut(), false);
}
void tst_qquicktext::strikeout()
{
- {
- QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
- QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
- QVERIFY(textObject != 0);
- QCOMPARE(textObject->font().strikeOut(), false);
-
- delete textObject;
- }
- {
- QString componentStr = "import QtQuick 2.0\nText { font.strikeout: true; text: \"Hello world!\" }";
- QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
- QVERIFY(textObject != 0);
- QCOMPARE(textObject->font().strikeOut(), true);
-
- delete textObject;
- }
+ QQuickView view(testFileUrl("strikeout.qml"));
+ view.show();
+ view.requestActivateWindow();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+ QQuickText *textObject = view.rootObject()->findChild<QQuickText*>("myText");
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->font().overline(), false);
+ QCOMPARE(textObject->font().underline(), false);
+ QCOMPARE(textObject->font().strikeOut(), true);
}
void tst_qquicktext::capitalization()
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != 0);
- QCOMPARE((int)textObject->font().capitalization(), (int)QQmlFontValueType::MixedCase);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::MixedCase);
delete textObject;
}
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != 0);
- QCOMPARE((int)textObject->font().capitalization(), (int)QQmlFontValueType::AllUppercase);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::AllUppercase);
delete textObject;
}
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != 0);
- QCOMPARE((int)textObject->font().capitalization(), (int)QQmlFontValueType::AllLowercase);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::AllLowercase);
delete textObject;
}
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != 0);
- QCOMPARE((int)textObject->font().capitalization(), (int)QQmlFontValueType::SmallCaps);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::SmallCaps);
delete textObject;
}
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != 0);
- QCOMPARE((int)textObject->font().capitalization(), (int)QQmlFontValueType::Capitalize);
+ QCOMPARE((int)textObject->font().capitalization(), (int)QQuickFontValueType::Capitalize);
delete textObject;
}
}
}
-
-
-
class EventSender : public QQuickItem
{
public:
mousePressEvent(event);
else if (event->type() == QEvent::MouseButtonRelease)
mouseReleaseEvent(event);
+ else if (event->type() == QEvent::MouseMove)
+ mouseMoveEvent(event);
else
qWarning() << "Trying to send unsupported event type";
}
void linkClicked(QString l) { link = l; }
};
-void tst_qquicktext::clickLink()
+class TextMetrics
{
+public:
+ TextMetrics(const QString &text, Qt::TextElideMode elideMode = Qt::ElideNone)
{
- QString componentStr = "import QtQuick 2.0\nText { text: \"<a href=\\\"http://qt.nokia.com\\\">Hello world!</a>\" }";
- QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QString adjustedText = text;
+ adjustedText.replace(QLatin1Char('\n'), QChar(QChar::LineSeparator));
+ if (elideMode == Qt::ElideLeft)
+ adjustedText = QChar(0x2026) + adjustedText;
+ else if (elideMode == Qt::ElideRight)
+ adjustedText = adjustedText + QChar(0x2026);
+
+ layout.setText(adjustedText);
+ QTextOption option;
+ option.setUseDesignMetrics(true);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ qreal height = 0;
+ QTextLine line = layout.createLine();
+ while (line.isValid()) {
+ line.setLineWidth(FLT_MAX);
+ line.setPosition(QPointF(0, height));
+ height += line.height();
+ line = layout.createLine();
+ }
+ layout.endLayout();
+ }
- QVERIFY(textObject != 0);
+ qreal width() const { return layout.maximumWidth(); }
- LinkTest test;
- QObject::connect(textObject, SIGNAL(linkActivated(QString)), &test, SLOT(linkClicked(QString)));
+ QRectF characterRectangle(
+ int position,
+ int hAlign = Qt::AlignLeft,
+ int vAlign = Qt::AlignTop,
+ const QSizeF &bounds = QSizeF(240, 320)) const
+ {
+ qreal dy = 0;
+ switch (vAlign) {
+ case Qt::AlignBottom:
+ dy = bounds.height() - layout.boundingRect().height();
+ break;
+ case Qt::AlignVCenter:
+ dy = (bounds.height() - layout.boundingRect().height()) / 2;
+ break;
+ default:
+ break;
+ }
- {
- QMouseEvent me(QEvent::MouseButtonPress,QPointF(textObject->x()/2, textObject->y()/2), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
- static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+ for (int i = 0; i < layout.lineCount(); ++i) {
+ QTextLine line = layout.lineAt(i);
+ if (position >= line.textStart() + line.textLength())
+ continue;
+ qreal dx = 0;
+ switch (hAlign) {
+ case Qt::AlignRight:
+ dx = bounds.width() - line.naturalTextWidth();
+ break;
+ case Qt::AlignHCenter:
+ dx = (bounds.width() - line.naturalTextWidth()) / 2;
+ break;
+ default:
+ break;
+ }
+ QRectF rect;
+ rect.setLeft(dx + line.cursorToX(position, QTextLine::Leading));
+ rect.setRight(dx + line.cursorToX(position, QTextLine::Trailing));
+ rect.setTop(dy + line.y());
+ rect.setBottom(dy + line.y() + line.height());
+
+ return rect;
}
+ return QRectF();
+ }
- {
- QMouseEvent me(QEvent::MouseButtonRelease,QPointF(textObject->x()/2, textObject->y()/2), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
- static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+ QTextLayout layout;
+};
- }
+typedef QVector<QPointF> PointVector;
+Q_DECLARE_METATYPE(PointVector);
- QCOMPARE(test.link, QLatin1String("http://qt.nokia.com"));
+void tst_qquicktext::clickLink_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<qreal>("width");
+ QTest::addColumn<QString>("bindings");
+ QTest::addColumn<PointVector>("mousePositions");
+ QTest::addColumn<QString>("link");
+
+ const QString singleLineText = "this text has a <a href=\\\"http://qt-project.org/single\\\">link</a> in it";
+ const QString singleLineLink = "http://qt-project.org/single";
+ const QString multipleLineText = "this text<br/>has <a href=\\\"http://qt-project.org/multiple\\\">multiple<br/>lines</a> in it";
+ const QString multipleLineLink = "http://qt-project.org/multiple";
+ const QString nestedText = "this text has a <a href=\\\"http://qt-project.org/outer\\\">nested <a href=\\\"http://qt-project.org/inner\\\">link</a> in it</a>";
+ const QString outerLink = "http://qt-project.org/outer";
+ const QString innerLink = "http://qt-project.org/inner";
- delete textObject;
+ {
+ const TextMetrics metrics("this text has a link in it");
+
+ QTest::newRow("click on link")
+ << singleLineText << 240.
+ << ""
+ << (PointVector() << metrics.characterRectangle(18).center())
+ << singleLineLink;
+ QTest::newRow("click on text")
+ << singleLineText << 240.
+ << ""
+ << (PointVector() << metrics.characterRectangle(13).center())
+ << QString();
+ QTest::newRow("drag within link")
+ << singleLineText << 240.
+ << ""
+ << (PointVector()
+ << metrics.characterRectangle(17).center()
+ << metrics.characterRectangle(19).center())
+ << singleLineLink;
+ QTest::newRow("drag away from link")
+ << singleLineText << 240.
+ << ""
+ << (PointVector()
+ << metrics.characterRectangle(18).center()
+ << metrics.characterRectangle(13).center())
+ << QString();
+ QTest::newRow("drag on to link")
+ << singleLineText << 240.
+ << ""
+ << (PointVector()
+ << metrics.characterRectangle(13).center()
+ << metrics.characterRectangle(18).center())
+ << QString();
+ QTest::newRow("click on bottom right aligned link")
+ << singleLineText << 240.
+ << "horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignBottom"
+ << (PointVector() << metrics.characterRectangle(18, Qt::AlignRight, Qt::AlignBottom).center())
+ << singleLineLink;
+ QTest::newRow("click on center aligned link")
+ << singleLineText << 240.
+ << "horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter"
+ << (PointVector() << metrics.characterRectangle(18, Qt::AlignHCenter, Qt::AlignVCenter).center())
+ << singleLineLink;
+ QTest::newRow("click on rich text link")
+ << singleLineText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector() << metrics.characterRectangle(18).center())
+ << singleLineLink;
+ QTest::newRow("click on rich text")
+ << singleLineText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector() << metrics.characterRectangle(13).center())
+ << QString();
+ QTest::newRow("click on bottom right aligned rich text link")
+ << singleLineText << 240.
+ << "textFormat: Text.RichText; horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignBottom"
+ << (PointVector() << metrics.characterRectangle(18, Qt::AlignRight, Qt::AlignBottom).center())
+ << singleLineLink;
+ QTest::newRow("click on center aligned rich text link")
+ << singleLineText << 240.
+ << "textFormat: Text.RichText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter"
+ << (PointVector() << metrics.characterRectangle(18, Qt::AlignHCenter, Qt::AlignVCenter).center())
+ << singleLineLink;
+ } {
+ const TextMetrics metrics("this text has a li", Qt::ElideRight);
+ QTest::newRow("click on right elided link")
+ << singleLineText << metrics.width() + 2
+ << "elide: Text.ElideRight"
+ << (PointVector() << metrics.characterRectangle(17).center())
+ << singleLineLink;
+ } {
+ const TextMetrics metrics("ink in it", Qt::ElideLeft);
+ QTest::newRow("click on left elided link")
+ << singleLineText << metrics.width() + 2
+ << "elide: Text.ElideLeft"
+ << (PointVector() << metrics.characterRectangle(2).center())
+ << singleLineLink;
+ } {
+ const TextMetrics metrics("this text\nhas multiple\nlines in it");
+ QTest::newRow("click on second line")
+ << multipleLineText << 240.
+ << ""
+ << (PointVector() << metrics.characterRectangle(18).center())
+ << multipleLineLink;
+ QTest::newRow("click on third line")
+ << multipleLineText << 240.
+ << ""
+ << (PointVector() << metrics.characterRectangle(25).center())
+ << multipleLineLink;
+ QTest::newRow("drag from second line to third")
+ << multipleLineText << 240.
+ << ""
+ << (PointVector()
+ << metrics.characterRectangle(18).center()
+ << metrics.characterRectangle(25).center())
+ << multipleLineLink;
+ QTest::newRow("click on rich text second line")
+ << multipleLineText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector() << metrics.characterRectangle(18).center())
+ << multipleLineLink;
+ QTest::newRow("click on rich text third line")
+ << multipleLineText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector() << metrics.characterRectangle(25).center())
+ << multipleLineLink;
+ QTest::newRow("drag rich text from second line to third")
+ << multipleLineText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector()
+ << metrics.characterRectangle(18).center()
+ << metrics.characterRectangle(25).center())
+ << multipleLineLink;
+ } {
+ const TextMetrics metrics("this text has a nested link in it");
+ QTest::newRow("click on left outer link")
+ << nestedText << 240.
+ << ""
+ << (PointVector() << metrics.characterRectangle(22).center())
+ << outerLink;
+ QTest::newRow("click on right outer link")
+ << nestedText << 240.
+ << ""
+ << (PointVector() << metrics.characterRectangle(27).center())
+ << outerLink;
+ QTest::newRow("click on inner link left")
+ << nestedText << 240.
+ << ""
+ << (PointVector() << metrics.characterRectangle(23).center())
+ << innerLink;
+ QTest::newRow("click on inner link right")
+ << nestedText << 240.
+ << ""
+ << (PointVector() << metrics.characterRectangle(26).center())
+ << innerLink;
+ QTest::newRow("drag from inner to outer link")
+ << nestedText << 240.
+ << ""
+ << (PointVector()
+ << metrics.characterRectangle(25).center()
+ << metrics.characterRectangle(30).center())
+ << QString();
+ QTest::newRow("drag from outer to inner link")
+ << nestedText << 240.
+ << ""
+ << (PointVector()
+ << metrics.characterRectangle(30).center()
+ << metrics.characterRectangle(25).center())
+ << QString();
+ QTest::newRow("click on left outer rich text link")
+ << nestedText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector() << metrics.characterRectangle(22).center())
+ << outerLink;
+ QTest::newRow("click on right outer rich text link")
+ << nestedText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector() << metrics.characterRectangle(27).center())
+ << outerLink;
+ QTest::newRow("click on inner rich text link left")
+ << nestedText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector() << metrics.characterRectangle(23).center())
+ << innerLink;
+ QTest::newRow("click on inner rich text link right")
+ << nestedText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector() << metrics.characterRectangle(26).center())
+ << innerLink;
+ QTest::newRow("drag from inner to outer rich text link")
+ << nestedText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector()
+ << metrics.characterRectangle(25).center()
+ << metrics.characterRectangle(30).center())
+ << QString();
+ QTest::newRow("drag from outer to inner rich text link")
+ << nestedText << 240.
+ << "textFormat: Text.RichText"
+ << (PointVector()
+ << metrics.characterRectangle(30).center()
+ << metrics.characterRectangle(25).center())
+ << QString();
}
}
+void tst_qquicktext::clickLink()
+{
+ QFETCH(QString, text);
+ QFETCH(qreal, width);
+ QFETCH(QString, bindings);
+ QFETCH(PointVector, mousePositions);
+ QFETCH(QString, link);
+
+ QString componentStr =
+ "import QtQuick 2.0\nText {\n"
+ "width: " + QString::number(width) + "\n"
+ "height: 320\n"
+ "text: \"" + text + "\"\n"
+ "" + bindings + "\n"
+ "}";
+ QQmlComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+
+ LinkTest test;
+ QObject::connect(textObject, SIGNAL(linkActivated(QString)), &test, SLOT(linkClicked(QString)));
+
+ QVERIFY(mousePositions.count() > 0);
+
+ QPointF mousePosition = mousePositions.first();
+ {
+ QMouseEvent me(QEvent::MouseButtonPress, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+ static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+ }
+
+ for (int i = 1; i < mousePositions.count(); ++i) {
+ mousePosition = mousePositions.at(i);
+
+ QMouseEvent me(QEvent::MouseMove, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+ static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+ }
+
+ {
+ QMouseEvent me(QEvent::MouseButtonRelease, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+ static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+ }
+
+ QCOMPARE(test.link, link);
+
+ delete textObject;
+}
+
void tst_qquicktext::baseUrl()
{
QUrl localUrl("file:///tests/text.qml");
QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
- << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error downloading http://127.0.0.1:14453/notexists.png - server replied: Not found";
+ << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error downloading " SERVER_ADDR "/notexists.png - server replied: Not found";
QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
}
QFETCH(QUrl, qmlfile);
QFETCH(QString, error);
- TestHTTPServer server(14453);
+ TestHTTPServer server(SERVER_PORT);
server.serveDirectory(testFile("http"));
if (!error.isEmpty())
QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
- QQmlComponent textComponent(&engine, qmlfile);
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QQuickView *view = new QQuickView(qmlfile);
+ view->show();
+ view->requestActivateWindow();
+ QVERIFY(QTest::qWaitForWindowActive(view));
+ QQuickText *textObject = qobject_cast<QQuickText*>(view->rootObject());
QVERIFY(textObject != 0);
-
QTRY_COMPARE(textObject->resourcesLoading(), 0);
QPixmap pm(testFile("http/exists.png"));
QCOMPARE(textObject->height(), 16.0);
}
- delete textObject;
+ delete view;
}
void tst_qquicktext::lineCount()
{
- QQuickView *canvas = createView(testFile("lineCount.qml"));
+ QQuickView *window = createView(testFile("lineCount.qml"));
- QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+ QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
QVERIFY(myText != 0);
QVERIFY(myText->lineCount() > 1);
QCOMPARE(myText->truncated(), true);
QCOMPARE(myText->maximumLineCount(), 2);
- delete canvas;
+ delete window;
}
void tst_qquicktext::lineHeight()
{
- QQuickView *canvas = createView(testFile("lineHeight.qml"));
+ QQuickView *window = createView(testFile("lineHeight.qml"));
- QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+ QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
QVERIFY(myText != 0);
QVERIFY(myText->lineHeight() == 1);
qreal h = myText->height();
myText->setLineHeight(1.5);
- QCOMPARE(myText->height(), qreal(qCeil(h * 1.5)));
+ QCOMPARE(myText->height(), qreal(qCeil(h)) * 1.5);
myText->setLineHeightMode(QQuickText::FixedHeight);
myText->setLineHeight(20);
myText->setLineHeight(10);
QCOMPARE(myText->height(), myText->lineCount() * 10.0);
- delete canvas;
+ delete window;
}
void tst_qquicktext::implicitSize_data()
QTest::addColumn<QString>("width");
QTest::addColumn<QString>("wrap");
QTest::addColumn<QString>("elide");
- QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.NoWrap" << "Text.ElideNone";
- QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 50" << "Text.NoWrap" << "Text.ElideNone";
- QTest::newRow("plain, 0 width") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.NoWrap" << "Text.ElideNone";
- QTest::newRow("plain, elide") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.NoWrap" << "Text.ElideRight";
- QTest::newRow("plain, 0 width, elide") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.NoWrap" << "Text.ElideRight";
- QTest::newRow("richtext, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 0" << "Text.NoWrap" << "Text.ElideNone";
- QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.Wrap" << "Text.ElideNone";
- QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "50" << "Text.Wrap" << "Text.ElideNone";
- QTest::newRow("plain_wrap, 0 width") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.Wrap" << "Text.ElideNone";
- QTest::newRow("plain_wrap, elide") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.Wrap" << "Text.ElideRight";
- QTest::newRow("plain_wrap, 0 width, elide") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.Wrap" << "Text.ElideRight";
- QTest::newRow("richtext_wrap, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "0" << "Text.Wrap" << "Text.ElideNone";
+ QTest::addColumn<QString>("format");
+ QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.NoWrap" << "Text.ElideNone" << "Text.PlainText";
+ QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 50" << "Text.NoWrap" << "Text.ElideNone" << "Text.RichText";
+ QTest::newRow("styledtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 50" << "Text.NoWrap" << "Text.ElideNone" << "Text.StyledText";
+ QTest::newRow("plain, 0 width") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.NoWrap" << "Text.ElideNone" << "Text.PlainText";
+ QTest::newRow("plain, elide") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.NoWrap" << "Text.ElideRight" << "Text.PlainText";
+ QTest::newRow("plain, 0 width, elide") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.NoWrap" << "Text.ElideRight" << "Text.PlainText";
+ QTest::newRow("richtext, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 0" << "Text.NoWrap" << "Text.ElideNone" << "Text.RichText";
+ QTest::newRow("styledtext, 0 width") << "<b>The quick red fox jumped over the lazy brown dog</b>" <<" 0" << "Text.NoWrap" << "Text.ElideNone" << "Text.StyledText";
+ QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.Wrap" << "Text.ElideNone" << "Text.PlainText";
+ QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "50" << "Text.Wrap" << "Text.ElideNone" << "Text.RichText";
+ QTest::newRow("styledtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "50" << "Text.Wrap" << "Text.ElideNone" << "Text.StyledText";
+ QTest::newRow("plain_wrap, 0 width") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.Wrap" << "Text.ElideNone" << "Text.PlainText";
+ QTest::newRow("plain_wrap, elide") << "The quick red fox jumped over the lazy brown dog" << "50" << "Text.Wrap" << "Text.ElideRight" << "Text.PlainText";
+ QTest::newRow("plain_wrap, 0 width, elide") << "The quick red fox jumped over the lazy brown dog" << "0" << "Text.Wrap" << "Text.ElideRight" << "Text.PlainText";
+ 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";
+ 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";
}
void tst_qquicktext::implicitSize()
{
QFETCH(QString, text);
QFETCH(QString, width);
+ QFETCH(QString, format);
QFETCH(QString, wrap);
QFETCH(QString, elide);
QString componentStr = "import QtQuick 2.0\nText { "
+ "property real iWidth: implicitWidth; "
"text: \"" + text + "\"; "
"width: " + width + "; "
+ "textFormat: " + format + "; "
"wrapMode: " + wrap + "; "
"elide: " + elide + "; "
- "maximumLineCount: 1 }";
+ "maximumLineCount: 2 }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject->width() < textObject->implicitWidth());
QVERIFY(textObject->height() == textObject->implicitHeight());
+ QCOMPARE(textObject->property("iWidth").toReal(), textObject->implicitWidth());
textObject->resetWidth();
QVERIFY(textObject->width() == textObject->implicitWidth());
QCOMPARE(spy.count(), ++spyCount);
}
+void tst_qquicktext::geometryChanged()
+{
+ // Test that text is re-laid out when the geometry of the item by verifying changes in content
+ // size. Implicit width is also tested as that in combination with item geometry provides a
+ // reference for expected content sizes.
+
+ QString componentStr = "import QtQuick 2.0\nText { font.family: \"__Qt__Box__Engine__\"; font.pixelSize: 10 }";
+ QQmlComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
+
+ const qreal implicitHeight = textObject->implicitHeight();
+
+ const qreal widths[] = { 100, 2000, 3000, -100, 100 };
+ const qreal heights[] = { implicitHeight, 2000, 3000, -implicitHeight, implicitHeight };
+
+ QCOMPARE(textObject->implicitWidth(), 0.);
+ QVERIFY(implicitHeight > 0.);
+ QCOMPARE(textObject->width(), textObject->implicitWidth());
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), textObject->implicitWidth());
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setText("The quick red fox jumped over the lazy brown dog");
+
+ const qreal implicitWidth = textObject->implicitWidth();
+
+ QVERIFY(implicitWidth > 0.);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), textObject->implicitWidth());
+ QCOMPARE(textObject->height(), textObject->implicitHeight());
+ QCOMPARE(textObject->contentWidth(), textObject->implicitWidth());
+ QCOMPARE(textObject->contentHeight(), textObject->implicitHeight());
+
+ // Changing the geometry with no eliding, or wrapping doesn't change the content size.
+ for (int i = 0; i < 5; ++i) {
+ textObject->setWidth(widths[i]);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), widths[i]);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), implicitWidth);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+ }
+
+ // With eliding enabled the content width is bounded to the item width, but is never
+ // larger than the implicit width.
+ textObject->setElideMode(QQuickText::ElideRight);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(2000.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), 2000.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), implicitWidth);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(3000.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), 3000.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), implicitWidth);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(-100);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), -100.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), 0.);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(100.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ // With wrapping enabled the implicit height changes with the width.
+ textObject->setElideMode(QQuickText::ElideNone);
+ textObject->setWrapMode(QQuickText::Wrap);
+ const qreal wrappedImplicitHeight = textObject->implicitHeight();
+
+ QVERIFY(wrappedImplicitHeight > implicitHeight);
+
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), wrappedImplicitHeight);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+
+ textObject->setWidth(2000.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), 2000.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), implicitWidth);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(3000.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), 3000.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), implicitWidth);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(-100);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), -100.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), implicitWidth); // 0 or negative width item won't wrap.
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(100.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), wrappedImplicitHeight);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+
+ // With no eliding or maximum line count the content height is the same as the implicit height.
+ for (int i = 0; i < 5; ++i) {
+ textObject->setHeight(heights[i]);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), heights[i]);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+ }
+
+ // The implicit height is unaffected by eliding but the content height will change.
+ textObject->setElideMode(QQuickText::ElideRight);
+
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setHeight(2000);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), 2000.);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+
+ textObject->setHeight(3000);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), 3000.);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+
+ textObject->setHeight(-implicitHeight);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), -implicitHeight);
+ QVERIFY(textObject->contentWidth() <= 0.);
+ QCOMPARE(textObject->contentHeight(), implicitHeight); // content height is never less than font height. seems a little odd in this instance.
+
+ textObject->setHeight(implicitHeight);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ // Varying the height with a maximum line count but no eliding won't affect the content height.
+ textObject->setElideMode(QQuickText::ElideNone);
+ textObject->setMaximumLineCount(2);
+ textObject->resetHeight();
+
+ const qreal maxLineCountImplicitHeight = textObject->implicitHeight();
+ QVERIFY(maxLineCountImplicitHeight > implicitHeight);
+ QVERIFY(maxLineCountImplicitHeight < wrappedImplicitHeight);
+
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), maxLineCountImplicitHeight);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
+
+ for (int i = 0; i < 5; ++i) {
+ textObject->setHeight(heights[i]);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), maxLineCountImplicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), heights[i]);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
+ }
+
+ // Varying the width with a maximum line count won't increase the implicit height beyond the
+ // height of the maximum number of lines.
+ textObject->setWidth(2000.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), 2000.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), implicitWidth);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(3000.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), 3000.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), implicitWidth);
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(-100);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), implicitHeight);
+ QCOMPARE(textObject->width(), -100.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QCOMPARE(textObject->contentWidth(), implicitWidth); // 0 or negative width item won't wrap.
+ QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+ textObject->setWidth(50.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), maxLineCountImplicitHeight);
+ QCOMPARE(textObject->width(), 50.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QVERIFY(textObject->contentWidth() <= 50.);
+ QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
+
+ textObject->setWidth(100.);
+ QCOMPARE(textObject->implicitWidth(), implicitWidth);
+ QCOMPARE(textObject->implicitHeight(), maxLineCountImplicitHeight);
+ QCOMPARE(textObject->width(), 100.);
+ QCOMPARE(textObject->height(), implicitHeight);
+ QVERIFY(textObject->contentWidth() <= 100.);
+ QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
+}
+
+void tst_qquicktext::implicitSizeBinding_data()
+{
+ implicitSize_data();
+}
+
+void tst_qquicktext::implicitSizeBinding()
+{
+ QFETCH(QString, text);
+ QFETCH(QString, wrap);
+ QFETCH(QString, format);
+ QString componentStr = "import QtQuick 2.0\nText { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + "; textFormat: " + format + " }";
+
+ QQmlComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
+
+ QCOMPARE(textObject->width(), textObject->implicitWidth());
+ QCOMPARE(textObject->height(), textObject->implicitHeight());
+
+ textObject->resetWidth();
+ QCOMPARE(textObject->width(), textObject->implicitWidth());
+ QCOMPARE(textObject->height(), textObject->implicitHeight());
+
+ textObject->resetHeight();
+ QCOMPARE(textObject->width(), textObject->implicitWidth());
+ QCOMPARE(textObject->height(), textObject->implicitHeight());
+}
+
+void tst_qquicktext::boundingRect_data()
+{
+ QTest::addColumn<QString>("format");
+ QTest::newRow("PlainText") << "Text.PlainText";
+ QTest::newRow("StyledText") << "Text.StyledText";
+ QTest::newRow("RichText") << "Text.RichText";
+}
+
+void tst_qquicktext::boundingRect()
+{
+ QFETCH(QString, format);
+
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0\n Text { textFormat:" + format.toUtf8() + "}", QUrl());
+ QScopedPointer<QObject> object(component.create());
+ QQuickText *text = qobject_cast<QQuickText *>(object.data());
+ QVERIFY(text);
+
+ QCOMPARE(text->boundingRect().x(), qreal(0));
+ QCOMPARE(text->boundingRect().y(), qreal(0));
+ QCOMPARE(text->boundingRect().width(), qreal(0));
+ QCOMPARE(text->boundingRect().height(), qreal(qCeil(QFontMetricsF(text->font()).height())));
+
+ text->setText("Hello World");
+
+ QTextLayout layout(text->text());
+ layout.setFont(text->font());
+
+ if (!qmlDisableDistanceField()) {
+ QTextOption option;
+ option.setUseDesignMetrics(true);
+ layout.setTextOption(option);
+ }
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ QCOMPARE(text->boundingRect().x(), qreal(0));
+ QCOMPARE(text->boundingRect().y(), qreal(0));
+ QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
+ QCOMPARE(text->boundingRect().height(), line.height());
+
+ // the size of the bounding rect shouldn't be bounded by the size of item.
+ text->setWidth(text->width() / 2);
+ QCOMPARE(text->boundingRect().x(), qreal(0));
+ QCOMPARE(text->boundingRect().y(), qreal(0));
+ QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
+ QCOMPARE(text->boundingRect().height(), line.height());
+
+ text->setHeight(text->height() * 2);
+ QCOMPARE(text->boundingRect().x(), qreal(0));
+ QCOMPARE(text->boundingRect().y(), qreal(0));
+ QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
+ QCOMPARE(text->boundingRect().height(), line.height());
+
+ text->setHAlign(QQuickText::AlignRight);
+ QCOMPARE(text->boundingRect().x(), text->width() - line.naturalTextWidth());
+ QCOMPARE(text->boundingRect().y(), qreal(0));
+ QCOMPARE(text->boundingRect().width(), line.naturalTextWidth());
+ QCOMPARE(text->boundingRect().height(), line.height());
+
+ text->setWrapMode(QQuickText::Wrap);
+ QCOMPARE(text->boundingRect().right(), text->width());
+ QCOMPARE(text->boundingRect().y(), qreal(0));
+ QVERIFY(text->boundingRect().width() < line.naturalTextWidth());
+ QVERIFY(text->boundingRect().height() > line.height());
+
+ text->setVAlign(QQuickText::AlignBottom);
+ QCOMPARE(text->boundingRect().right(), text->width());
+ QCOMPARE(text->boundingRect().bottom(), text->height());
+ QVERIFY(text->boundingRect().width() < line.naturalTextWidth());
+ QVERIFY(text->boundingRect().height() > line.height());
+}
+
+void tst_qquicktext::clipRect()
+{
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0\n Text {}", QUrl());
+ QScopedPointer<QObject> object(component.create());
+ QQuickText *text = qobject_cast<QQuickText *>(object.data());
+ QVERIFY(text);
+
+ QTextLayout layout;
+ layout.setFont(text->font());
+
+ QCOMPARE(text->clipRect().x(), qreal(0));
+ QCOMPARE(text->clipRect().y(), qreal(0));
+ QCOMPARE(text->clipRect().width(), text->width());
+ QCOMPARE(text->clipRect().height(), text->height());
+
+ text->setText("Hello World");
+
+ QCOMPARE(text->clipRect().x(), qreal(0));
+ QCOMPARE(text->clipRect().y(), qreal(0));
+ QCOMPARE(text->clipRect().width(), text->width());
+ QCOMPARE(text->clipRect().height(), text->height());
+
+ // Clip rect follows the item not content dimensions.
+ text->setWidth(text->width() / 2);
+ QCOMPARE(text->clipRect().x(), qreal(0));
+ QCOMPARE(text->clipRect().y(), qreal(0));
+ QCOMPARE(text->clipRect().width(), text->width());
+ QCOMPARE(text->clipRect().height(), text->height());
+
+ text->setHeight(text->height() * 2);
+ QCOMPARE(text->clipRect().x(), qreal(0));
+ QCOMPARE(text->clipRect().y(), qreal(0));
+ QCOMPARE(text->clipRect().width(), text->width());
+ QCOMPARE(text->clipRect().height(), text->height());
+
+ // Setting a style adds a small amount of padding to the clip rect.
+ text->setStyle(QQuickText::Outline);
+ QCOMPARE(text->clipRect().x(), qreal(-1));
+ QCOMPARE(text->clipRect().y(), qreal(0));
+ QCOMPARE(text->clipRect().width(), text->width() + 2);
+ QCOMPARE(text->clipRect().height(), text->height() + 2);
+}
+
void tst_qquicktext::lineLaidOut()
{
- QQuickView *canvas = createView(testFile("lineLayout.qml"));
+ QQuickView *window = createView(testFile("lineLayout.qml"));
- QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+ QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
QVERIFY(myText != 0);
QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
QVERIFY(!textPrivate->extra.isAllocated());
-#if defined(Q_OS_MAC)
- QVERIFY(myText->lineCount() == textPrivate->linesRects.count());
-#endif
-
for (int i = 0; i < textPrivate->layout.lineCount(); ++i) {
QRectF r = textPrivate->layout.lineAt(i).rect();
QVERIFY(r.width() == i * 15);
}
}
- delete canvas;
+ delete window;
+}
+
+void tst_qquicktext::lineLaidOutRelayout()
+{
+ QQuickView *window = createView(testFile("lineLayoutRelayout.qml"));
+
+ window->show();
+ window->requestActivateWindow();
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
+ QVERIFY(myText != 0);
+
+ QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
+ QVERIFY(textPrivate != 0);
+
+ QVERIFY(!textPrivate->extra.isAllocated());
+
+ qreal maxH = 0;
+ for (int i = 0; i < textPrivate->layout.lineCount(); ++i) {
+ QRectF r = textPrivate->layout.lineAt(i).rect();
+
+ if (r.x() == 0) {
+ QCOMPARE(r.y(), i * r.height());
+ maxH = qMax(maxH, r.y() + r.height());
+ } else {
+ QCOMPARE(r.x(), myText->width() / 2);
+ QCOMPARE(r.y(), (i * r.height()) - maxH);
+ }
+ }
+
+ delete window;
}
void tst_qquicktext::imgTagsBaseUrl_data()
<< 181.;
QTest::newRow("absolute remote")
- << QUrl("http://127.0.0.1:14453/images/heart200.png")
+ << QUrl(SERVER_ADDR "/images/heart200.png")
<< QUrl()
<< QUrl()
<< 181.;
QTest::newRow("relative remote base 1")
<< QUrl("images/heart200.png")
- << QUrl("http://127.0.0.1:14453/")
+ << QUrl(SERVER_ADDR "/")
<< testFileUrl("nonexistant/app.qml")
<< 181.;
QTest::newRow("relative remote base 2")
<< QUrl("heart200.png")
- << QUrl("http://127.0.0.1:14453/images/")
+ << QUrl(SERVER_ADDR "/images/")
<< testFileUrl("nonexistant/app.qml")
<< 181.;
}
QFETCH(QUrl, contextUrl);
QFETCH(qreal, imgHeight);
- TestHTTPServer server(14453);
+ TestHTTPServer server(SERVER_PORT);
server.serveDirectory(testFile(""));
QByteArray baseUrlFragment;
void tst_qquicktext::imgTagsElide()
{
- QQuickView *canvas = createView(testFile("imgTagsElide.qml"));
- QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+ QQuickView *window = createView(testFile("imgTagsElide.qml"));
+ QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
QVERIFY(myText != 0);
QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
QTRY_VERIFY(textPrivate->visibleImgTags.count() == 1);
delete myText;
- delete canvas;
+ delete window;
}
void tst_qquicktext::imgTagsUpdates()
{
- QQuickView *canvas = createView(testFile("imgTagsUpdates.qml"));
- QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+ QQuickView *window = createView(testFile("imgTagsUpdates.qml"));
+ QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
QVERIFY(myText != 0);
QSignalSpy spy(myText, SIGNAL(contentSizeChanged()));
QVERIFY(spy.count() == 3);
delete myText;
- delete canvas;
+ delete window;
}
void tst_qquicktext::imgTagsError()
{
QFETCH(QString, text);
- QScopedPointer<QQuickView> canvas(createView(testFile("fontSizeMode.qml")));
- canvas->show();
+ QScopedPointer<QQuickView> window(createView(testFile("fontSizeMode.qml")));
+ window->show();
- QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+ QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
QVERIFY(myText != 0);
myText->setText(text);
{
QFETCH(QString, text);
- QScopedPointer<QQuickView> canvas(createView(testFile("fontSizeMode.qml")));
- canvas->show();
+ QScopedPointer<QQuickView> window(createView(testFile("fontSizeMode.qml")));
+ window->show();
- QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+ QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
QVERIFY(myText != 0);
myText->setText(text);
{
QFETCH(QString, source);
- QScopedPointer<QQuickView> canvas(createView(source));
- canvas->show();
+ QScopedPointer<QQuickView> window(createView(source));
+ window->show();
- QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+ QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
QVERIFY(myText != 0);
const QString longText = "the quick brown fox jumped over the lazy dog";
delete view;
}
+typedef qreal (*ExpectedBaseline)(QQuickText *item);
+Q_DECLARE_METATYPE(ExpectedBaseline)
+
+static qreal expectedBaselineTop(QQuickText *item)
+{
+ QFontMetricsF fm(item->font());
+ return fm.ascent();
+}
+
+static qreal expectedBaselineBottom(QQuickText *item)
+{
+ QFontMetricsF fm(item->font());
+ return item->height() - item->contentHeight() + fm.ascent();
+}
+
+static qreal expectedBaselineCenter(QQuickText *item)
+{
+ QFontMetricsF fm(item->font());
+ return ((item->height() - item->contentHeight()) / 2) + fm.ascent();
+}
+
+static qreal expectedBaselineBold(QQuickText *item)
+{
+ QFont font = item->font();
+ font.setBold(true);
+ QFontMetricsF fm(font);
+ return fm.ascent();
+}
+
+static qreal expectedBaselineImage(QQuickText *item)
+{
+ QFontMetricsF fm(item->font());
+ // The line is positioned so the bottom of the line is aligned with the bottom of the image,
+ // or image height - line height and the baseline is line position + ascent. Because
+ // QTextLine's height is rounded up this can give slightly different results to image height
+ // - descent.
+ return 181 - qCeil(fm.height()) + fm.ascent();
+}
+
+static qreal expectedBaselineCustom(QQuickText *item)
+{
+ QFontMetricsF fm(item->font());
+ return 16 + fm.ascent();
+}
+
+static qreal expectedBaselineScaled(QQuickText *item)
+{
+ QFont font = item->font();
+ QTextLayout layout(item->text().replace(QLatin1Char('\n'), QChar::LineSeparator));
+ do {
+ layout.setFont(font);
+ qreal width = 0;
+ layout.beginLayout();
+ for (QTextLine line = layout.createLine(); line.isValid(); line = layout.createLine()) {
+ line.setLineWidth(FLT_MAX);
+ width = qMax(line.naturalTextWidth(), width);
+ }
+ layout.endLayout();
+
+ if (width < item->width()) {
+ QFontMetricsF fm(layout.font());
+ return fm.ascent();
+ }
+ font.setPointSize(font.pointSize() - 1);
+ } while (font.pointSize() > 0);
+ return 0;
+}
+
+static qreal expectedBaselineFixedBottom(QQuickText *item)
+{
+ QFontMetricsF fm(item->font());
+ qreal dy = item->text().contains(QLatin1Char('\n'))
+ ? 160
+ : 180;
+ return dy + fm.ascent();
+}
+
+static qreal expectedBaselineProportionalBottom(QQuickText *item)
+{
+ QFontMetricsF fm(item->font());
+ qreal dy = item->text().contains(QLatin1Char('\n'))
+ ? 200 - (qCeil(fm.height()) * 3)
+ : 200 - (qCeil(fm.height()) * 1.5);
+ return dy + fm.ascent();
+}
+
+void tst_qquicktext::baselineOffset_data()
+{
+ qRegisterMetaType<ExpectedBaseline>();
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<QString>("wrappedText");
+ QTest::addColumn<QByteArray>("bindings");
+ QTest::addColumn<ExpectedBaseline>("expectedBaseline");
+ QTest::addColumn<ExpectedBaseline>("expectedBaselineEmpty");
+
+ QTest::newRow("top align")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("height: 200; verticalAlignment: Text.AlignTop")
+ << &expectedBaselineTop
+ << &expectedBaselineTop;
+ QTest::newRow("bottom align")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("height: 200; verticalAlignment: Text.AlignBottom")
+ << &expectedBaselineBottom
+ << &expectedBaselineBottom;
+ QTest::newRow("center align")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("height: 200; verticalAlignment: Text.AlignVCenter")
+ << &expectedBaselineCenter
+ << &expectedBaselineCenter;
+
+ QTest::newRow("bold")
+ << "<b>hello world</b>"
+ << "<b>hello<br/>world</b>"
+ << QByteArray("height: 200")
+ << &expectedBaselineTop
+ << &expectedBaselineBold;
+
+ QTest::newRow("richText")
+ << "<b>hello world</b>"
+ << "<b>hello<br/>world</b>"
+ << QByteArray("height: 200; textFormat: Text.RichText")
+ << &expectedBaselineTop
+ << &expectedBaselineTop;
+
+ QTest::newRow("elided")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("width: 20; height: 8; elide: Text.ElideRight")
+ << &expectedBaselineTop
+ << &expectedBaselineTop;
+
+ QTest::newRow("elided bottom align")
+ << "hello world"
+ << "hello\nworld!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
+ << QByteArray("width: 200; height: 200; elide: Text.ElideRight; verticalAlignment: Text.AlignBottom")
+ << &expectedBaselineBottom
+ << &expectedBaselineBottom;
+
+ QTest::newRow("image")
+ << "hello <img src=\"images/heart200.png\" /> world"
+ << "hello <img src=\"images/heart200.png\" /><br/>world"
+ << QByteArray("height: 200\n; baseUrl: \"") + testFileUrl("reference").toEncoded() + QByteArray("\"")
+ << &expectedBaselineImage
+ << &expectedBaselineTop;
+
+ QTest::newRow("customLine")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("height: 200; onLineLaidOut: line.y += 16")
+ << &expectedBaselineCustom
+ << &expectedBaselineCustom;
+
+ QTest::newRow("scaled font")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("width: 200; minimumPointSize: 1; font.pointSize: 64; fontSizeMode: Text.HorizontalFit")
+ << &expectedBaselineScaled
+ << &expectedBaselineTop;
+
+ QTest::newRow("fixed line height top align")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("height: 200; lineHeightMode: Text.FixedHeight; lineHeight: 20; verticalAlignment: Text.AlignTop")
+ << &expectedBaselineTop
+ << &expectedBaselineTop;
+
+ QTest::newRow("fixed line height bottom align")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("height: 200; lineHeightMode: Text.FixedHeight; lineHeight: 20; verticalAlignment: Text.AlignBottom")
+ << &expectedBaselineFixedBottom
+ << &expectedBaselineFixedBottom;
+
+ QTest::newRow("proportional line height top align")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("height: 200; lineHeightMode: Text.ProportionalHeight; lineHeight: 1.5; verticalAlignment: Text.AlignTop")
+ << &expectedBaselineTop
+ << &expectedBaselineTop;
+
+ QTest::newRow("proportional line height bottom align")
+ << "hello world"
+ << "hello\nworld"
+ << QByteArray("height: 200; lineHeightMode: Text.ProportionalHeight; lineHeight: 1.5; verticalAlignment: Text.AlignBottom")
+ << &expectedBaselineProportionalBottom
+ << &expectedBaselineProportionalBottom;
+}
+
+void tst_qquicktext::baselineOffset()
+{
+ QFETCH(QString, text);
+ QFETCH(QString, wrappedText);
+ QFETCH(QByteArray, bindings);
+ QFETCH(ExpectedBaseline, expectedBaseline);
+ QFETCH(ExpectedBaseline, expectedBaselineEmpty);
+
+ QQmlComponent component(&engine);
+ component.setData(
+ "import QtQuick 2.0\n"
+ "Text {\n"
+ + bindings + "\n"
+ "}", QUrl());
+
+ QScopedPointer<QObject> object(component.create());
+
+ QQuickText *item = qobject_cast<QQuickText *>(object.data());
+ QVERIFY(item);
+
+ {
+ qreal baseline = expectedBaselineEmpty(item);
+
+ QCOMPARE(item->baselineOffset(), baseline);
+
+ item->setText(text);
+ if (expectedBaseline != expectedBaselineEmpty)
+ baseline = expectedBaseline(item);
+
+ QCOMPARE(item->baselineOffset(), baseline);
+
+ item->setText(wrappedText);
+ QCOMPARE(item->baselineOffset(), expectedBaseline(item));
+ }
+
+ QFont font = item->font();
+ font.setPointSize(font.pointSize() + 8);
+
+ {
+ QCOMPARE(item->baselineOffset(), expectedBaseline(item));
+
+ item->setText(text);
+ qreal baseline = expectedBaseline(item);
+ QCOMPARE(item->baselineOffset(), baseline);
+
+ item->setText(QString());
+ if (expectedBaselineEmpty != expectedBaseline)
+ baseline = expectedBaselineEmpty(item);
+
+ QCOMPARE(item->baselineOffset(), baseline);
+ }
+}
+
+void tst_qquicktext::htmlLists()
+{
+ QFETCH(QString, text);
+ QFETCH(int, nbLines);
+
+ QQuickView *view = createView(testFile("htmlLists.qml"));
+ QQuickText *textObject = view->rootObject()->findChild<QQuickText*>("myText");
+
+ QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
+ QVERIFY(textPrivate != 0);
+ QVERIFY(textPrivate->extra.isAllocated());
+
+ QVERIFY(textObject != 0);
+ textObject->setText(text);
+
+ view->show();
+ view->requestActivateWindow();
+ QVERIFY(QTest::qWaitForWindowActive(view));
+
+ QCOMPARE(textPrivate->extra->doc->lineCount(), nbLines);
+
+ delete view;
+}
+
+void tst_qquicktext::htmlLists_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<int>("nbLines");
+
+ QTest::newRow("ordered list") << "<ol><li>one<li>two<li>three" << 3;
+ QTest::newRow("ordered list closed") << "<ol><li>one</li></ol>" << 1;
+ QTest::newRow("ordered list alpha") << "<ol type=\"a\"><li>one</li><li>two</li></ol>" << 2;
+ QTest::newRow("ordered list upper alpha") << "<ol type=\"A\"><li>one</li><li>two</li></ol>" << 2;
+ QTest::newRow("ordered list roman") << "<ol type=\"i\"><li>one</li><li>two</li></ol>" << 2;
+ QTest::newRow("ordered list upper roman") << "<ol type=\"I\"><li>one</li><li>two</li></ol>" << 2;
+ QTest::newRow("ordered list bad") << "<ol type=\"z\"><li>one</li><li>two</li></ol>" << 2;
+ QTest::newRow("unordered list") << "<ul><li>one<li>two" << 2;
+ QTest::newRow("unordered list closed") << "<ul><li>one</li><li>two</li></ul>" << 2;
+ QTest::newRow("unordered list disc") << "<ul type=\"disc\"><li>one</li><li>two</li></ul>" << 2;
+ QTest::newRow("unordered list square") << "<ul type=\"square\"><li>one</li><li>two</li></ul>" << 2;
+ QTest::newRow("unordered list bad") << "<ul type=\"bad\"><li>one</li><li>two</li></ul>" << 2;
+}
+
QTEST_MAIN(tst_qquicktext)
#include "tst_qquicktext.moc"