1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include <QtTest/QSignalSpy>
43 #include "../../../shared/util.h"
44 #include "../shared/testhttpserver.h"
47 #include <QTextDocument>
48 #include <QtDeclarative/qdeclarativeengine.h>
49 #include <QtDeclarative/qdeclarativecontext.h>
50 #include <QtDeclarative/qdeclarativeexpression.h>
51 #include <QtDeclarative/qdeclarativecomponent.h>
52 #include <QtGui/qguiapplication.h>
53 #include <private/qsgtextedit_p.h>
54 #include <private/qsgtextedit_p_p.h>
55 #include <private/qsgdistancefieldglyphcache_p.h>
56 #include <QFontMetrics>
60 #include <QInputContext>
61 #include <QInputPanel>
64 #include <private/qtextcontrol_p.h>
65 #include "../shared/util.h"
68 #include <Carbon/Carbon.h>
72 #define QTBUG_21691_MESSAGE "QTBUG-21691: The test needs to be rewritten to not use QInputContext"
74 Q_DECLARE_METATYPE(QSGTextEdit::SelectionMode)
75 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
77 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
79 // XXX This will be replaced by some clever persistent platform image store.
80 QString persistent_dir = TESTDATA("");
81 QString arch = "unknown-architecture"; // QTest needs to help with this.
83 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
85 if (!QFile::exists(expectfile)) {
86 actual.save(expectfile);
87 qWarning() << "created" << expectfile;
94 class tst_qsgtextedit : public QObject
103 void cleanupTestCase();
109 void alignments_data();
111 // ### these tests may be trivial
113 void hAlign_RightToLeft();
118 void persistentSelection();
121 void isRightToLeft_data();
122 void isRightToLeft();
124 void moveCursorSelection_data();
125 void moveCursorSelection();
126 void moveCursorSelectionSequence_data();
127 void moveCursorSelectionSequence();
128 void mouseSelection_data();
129 void mouseSelection();
130 void mouseSelectionMode_data();
131 void mouseSelectionMode();
132 void dragMouseSelection();
133 void inputMethodHints();
137 void cursorDelegate();
138 void cursorVisible();
139 void delegateLoading_data();
140 void delegateLoading();
145 void canPasteEmpty();
147 void openInputPanel();
148 void geometrySignals();
149 void pastingRichText_QTBUG_14003();
150 void implicitSize_data();
152 void testQtQuick11Attributes();
153 void testQtQuick11Attributes_data();
155 void preeditMicroFocus();
156 void inputContextMouseHandler();
157 void inputMethodComposing();
158 void cursorRectangleSize();
161 void simulateKey(QSGView *, int key, Qt::KeyboardModifiers modifiers = 0);
163 QStringList standard;
164 QStringList richText;
166 QStringList hAlignmentStrings;
167 QStringList vAlignmentStrings;
169 QList<Qt::Alignment> vAlignments;
170 QList<Qt::Alignment> hAlignments;
172 QStringList colorStrings;
174 QDeclarativeEngine engine;
176 void tst_qsgtextedit::initTestCase()
180 void tst_qsgtextedit::cleanupTestCase()
184 tst_qsgtextedit::tst_qsgtextedit()
186 standard << "the quick brown fox jumped over the lazy dog"
187 << "the quick brown fox\n jumped over the lazy dog"
191 richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
192 << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
194 hAlignmentStrings << "AlignLeft"
198 vAlignmentStrings << "AlignTop"
202 hAlignments << Qt::AlignLeft
206 vAlignments << Qt::AlignTop
210 colorStrings << "aliceblue"
223 // need a different test to do alpha channel test
229 void tst_qsgtextedit::text()
232 QDeclarativeComponent texteditComponent(&engine);
233 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl());
234 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
236 QVERIFY(textEditObject != 0);
237 QCOMPARE(textEditObject->text(), QString(""));
240 for (int i = 0; i < standard.size(); i++)
242 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
243 QDeclarativeComponent texteditComponent(&engine);
244 texteditComponent.setData(componentStr.toLatin1(), QUrl());
245 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
247 QVERIFY(textEditObject != 0);
248 QCOMPARE(textEditObject->text(), standard.at(i));
251 for (int i = 0; i < richText.size(); i++)
253 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
254 QDeclarativeComponent texteditComponent(&engine);
255 texteditComponent.setData(componentStr.toLatin1(), QUrl());
256 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
258 QVERIFY(textEditObject != 0);
259 QString actual = textEditObject->text();
260 QString expected = richText.at(i);
261 actual.replace(QRegExp(".*<body[^>]*>"),"");
262 actual.replace(QRegExp("(<[^>]*>)+"),"<>");
263 expected.replace(QRegExp("(<[^>]*>)+"),"<>");
264 QCOMPARE(actual.simplified(),expected.simplified());
268 void tst_qsgtextedit::width()
270 // uses Font metrics to find the width for standard and document to find the width for rich
272 QDeclarativeComponent texteditComponent(&engine);
273 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl());
274 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
276 QVERIFY(textEditObject != 0);
277 QCOMPARE(textEditObject->width(), 0.0);
280 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
282 for (int i = 0; i < standard.size(); i++)
285 qreal metricWidth = 0.0;
287 if (requiresUnhintedMetrics) {
288 QString s = standard.at(i);
289 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
291 QTextLayout layout(s);
292 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
295 option.setUseDesignMetrics(true);
296 layout.setTextOption(option);
299 layout.beginLayout();
301 QTextLine line = layout.createLine();
308 metricWidth = ceil(layout.boundingRect().width());
311 metricWidth = fm.size(Qt::TextExpandTabs | Qt::TextShowMnemonic, standard.at(i)).width();
312 metricWidth = ceil(metricWidth);
315 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
316 QDeclarativeComponent texteditComponent(&engine);
317 texteditComponent.setData(componentStr.toLatin1(), QUrl());
318 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
320 QVERIFY(textEditObject != 0);
321 QCOMPARE(textEditObject->width(), qreal(metricWidth));
324 for (int i = 0; i < richText.size(); i++)
326 QTextDocument document;
327 document.setHtml(richText.at(i));
328 document.setDocumentMargin(0);
329 if (requiresUnhintedMetrics)
330 document.setUseDesignMetrics(true);
332 int documentWidth = ceil(document.idealWidth());
334 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
335 QDeclarativeComponent texteditComponent(&engine);
336 texteditComponent.setData(componentStr.toLatin1(), QUrl());
337 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
339 QVERIFY(textEditObject != 0);
340 QCOMPARE(textEditObject->width(), qreal(documentWidth));
344 void tst_qsgtextedit::wrap()
346 // for specified width and wrap set true
348 QDeclarativeComponent texteditComponent(&engine);
349 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\"; wrapMode: TextEdit.WordWrap; width: 300 }", QUrl());
350 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
352 QVERIFY(textEditObject != 0);
353 QCOMPARE(textEditObject->width(), 300.);
356 for (int i = 0; i < standard.size(); i++)
358 QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + standard.at(i) + "\" }";
359 QDeclarativeComponent texteditComponent(&engine);
360 texteditComponent.setData(componentStr.toLatin1(), QUrl());
361 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
363 QVERIFY(textEditObject != 0);
364 QCOMPARE(textEditObject->width(), 300.);
367 for (int i = 0; i < richText.size(); i++)
369 QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + richText.at(i) + "\" }";
370 QDeclarativeComponent texteditComponent(&engine);
371 texteditComponent.setData(componentStr.toLatin1(), QUrl());
372 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
374 QVERIFY(textEditObject != 0);
375 QCOMPARE(textEditObject->width(), 300.);
380 void tst_qsgtextedit::textFormat()
383 QDeclarativeComponent textComponent(&engine);
384 textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
385 QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
387 QVERIFY(textObject != 0);
388 QVERIFY(textObject->textFormat() == QSGTextEdit::RichText);
391 QDeclarativeComponent textComponent(&engine);
392 textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
393 QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
395 QVERIFY(textObject != 0);
396 QVERIFY(textObject->textFormat() == QSGTextEdit::PlainText);
400 void tst_qsgtextedit::alignments_data()
402 QTest::addColumn<int>("hAlign");
403 QTest::addColumn<int>("vAlign");
404 QTest::addColumn<QString>("expectfile");
406 QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << "alignments_lt";
407 QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << "alignments_rt";
408 QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << "alignments_ct";
410 QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << "alignments_lb";
411 QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << "alignments_rb";
412 QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << "alignments_cb";
414 QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << "alignments_lc";
415 QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << "alignments_rc";
416 QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << "alignments_cc";
420 void tst_qsgtextedit::alignments()
422 QSKIP("Image comparison of text is almost guaranteed to fail during development", SkipAll);
426 QFETCH(QString, expectfile);
428 QSGView canvas(QUrl::fromLocalFile(TESTDATA("alignments.qml")));
431 canvas.requestActivateWindow();
432 QTest::qWaitForWindowShown(&canvas);
433 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
435 QObject *ob = canvas.rootObject();
437 ob->setProperty("horizontalAlignment",hAlign);
438 ob->setProperty("verticalAlignment",vAlign);
439 QTRY_COMPARE(ob->property("running").toBool(),false);
440 QImage actual = canvas.grabFrameBuffer();
442 expectfile = createExpectedFileIfNotFound(expectfile, actual);
444 QImage expect(expectfile);
446 QCOMPARE(actual,expect);
450 //the alignment tests may be trivial o.oa
451 void tst_qsgtextedit::hAlign()
453 //test one align each, and then test if two align fails.
455 for (int i = 0; i < standard.size(); i++)
457 for (int j=0; j < hAlignmentStrings.size(); j++)
459 QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
460 QDeclarativeComponent texteditComponent(&engine);
461 texteditComponent.setData(componentStr.toLatin1(), QUrl());
462 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
464 QVERIFY(textEditObject != 0);
465 QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
469 for (int i = 0; i < richText.size(); i++)
471 for (int j=0; j < hAlignmentStrings.size(); j++)
473 QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
474 QDeclarativeComponent texteditComponent(&engine);
475 texteditComponent.setData(componentStr.toLatin1(), QUrl());
476 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
478 QVERIFY(textEditObject != 0);
479 QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
485 void tst_qsgtextedit::hAlign_RightToLeft()
487 QSGView canvas(QUrl::fromLocalFile(TESTDATA("horizontalAlignment_RightToLeft.qml")));
488 QSGTextEdit *textEdit = canvas.rootObject()->findChild<QSGTextEdit*>("text");
489 QVERIFY(textEdit != 0);
492 const QString rtlText = textEdit->text();
494 // implicit alignment should follow the reading direction of text
495 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
496 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
498 // explicitly left aligned
499 textEdit->setHAlign(QSGTextEdit::AlignLeft);
500 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
501 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
503 // explicitly right aligned
504 textEdit->setHAlign(QSGTextEdit::AlignRight);
505 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
506 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
508 QString textString = textEdit->text();
509 textEdit->setText(QString("<i>") + textString + QString("</i>"));
510 textEdit->resetHAlign();
512 // implicitly aligned rich text should follow the reading direction of RTL text
513 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
514 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
515 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
517 // explicitly left aligned rich text
518 textEdit->setHAlign(QSGTextEdit::AlignLeft);
519 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
520 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
521 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
523 // explicitly right aligned rich text
524 textEdit->setHAlign(QSGTextEdit::AlignRight);
525 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
526 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
527 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
529 textEdit->setText(textString);
531 // explicitly center aligned
532 textEdit->setHAlign(QSGTextEdit::AlignHCenter);
533 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignHCenter);
534 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
536 // reseted alignment should go back to following the text reading direction
537 textEdit->resetHAlign();
538 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
539 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
541 // mirror the text item
542 QSGItemPrivate::get(textEdit)->setLayoutMirror(true);
544 // mirrored implicit alignment should continue to follow the reading direction of the text
545 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
546 QCOMPARE(textEdit->effectiveHAlign(), QSGTextEdit::AlignRight);
547 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
549 // mirrored explicitly right aligned behaves as left aligned
550 textEdit->setHAlign(QSGTextEdit::AlignRight);
551 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
552 QCOMPARE(textEdit->effectiveHAlign(), QSGTextEdit::AlignLeft);
553 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
555 // mirrored explicitly left aligned behaves as right aligned
556 textEdit->setHAlign(QSGTextEdit::AlignLeft);
557 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
558 QCOMPARE(textEdit->effectiveHAlign(), QSGTextEdit::AlignRight);
559 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
562 QSGItemPrivate::get(textEdit)->setLayoutMirror(false);
563 textEdit->resetHAlign();
565 // English text should be implicitly left aligned
566 textEdit->setText("Hello world!");
567 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
568 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
570 canvas.requestActivateWindow();
571 QTest::qWaitForWindowShown(&canvas);
572 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
574 textEdit->setText(QString());
575 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(&canvas, &ev); }
576 QEXPECT_FAIL("", "QTBUG-21691", Abort);
577 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
578 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(&canvas, &ev); }
579 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
581 #ifndef Q_OS_MAC // QTBUG-18040
582 // empty text with implicit alignment follows the system locale-based
583 // keyboard input direction from QGuiApplication::keyboardInputDirection
584 textEdit->setText("");
585 QCOMPARE(textEdit->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
586 QSGTextEdit::AlignLeft : QSGTextEdit::AlignRight);
587 if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight)
588 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
590 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
591 textEdit->setHAlign(QSGTextEdit::AlignRight);
592 QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
593 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
596 #ifndef Q_OS_MAC // QTBUG-18040
597 // alignment of TextEdit with no text set to it
598 QString componentStr = "import QtQuick 2.0\nTextEdit {}";
599 QDeclarativeComponent textComponent(&engine);
600 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
601 QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
602 QCOMPARE(textObject->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
603 QSGTextEdit::AlignLeft : QSGTextEdit::AlignRight);
608 void tst_qsgtextedit::vAlign()
610 //test one align each, and then test if two align fails.
612 for (int i = 0; i < standard.size(); i++)
614 for (int j=0; j < vAlignmentStrings.size(); j++)
616 QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
617 QDeclarativeComponent texteditComponent(&engine);
618 texteditComponent.setData(componentStr.toLatin1(), QUrl());
619 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
621 QVERIFY(textEditObject != 0);
622 QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
626 for (int i = 0; i < richText.size(); i++)
628 for (int j=0; j < vAlignmentStrings.size(); j++)
630 QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
631 QDeclarativeComponent texteditComponent(&engine);
632 texteditComponent.setData(componentStr.toLatin1(), QUrl());
633 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
635 QVERIFY(textEditObject != 0);
636 QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
642 void tst_qsgtextedit::font()
644 //test size, then bold, then italic, then family
646 QString componentStr = "import QtQuick 2.0\nTextEdit { font.pointSize: 40; text: \"Hello World\" }";
647 QDeclarativeComponent texteditComponent(&engine);
648 texteditComponent.setData(componentStr.toLatin1(), QUrl());
649 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
651 QVERIFY(textEditObject != 0);
652 QCOMPARE(textEditObject->font().pointSize(), 40);
653 QCOMPARE(textEditObject->font().bold(), false);
654 QCOMPARE(textEditObject->font().italic(), false);
658 QString componentStr = "import QtQuick 2.0\nTextEdit { font.bold: true; text: \"Hello World\" }";
659 QDeclarativeComponent texteditComponent(&engine);
660 texteditComponent.setData(componentStr.toLatin1(), QUrl());
661 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
663 QVERIFY(textEditObject != 0);
664 QCOMPARE(textEditObject->font().bold(), true);
665 QCOMPARE(textEditObject->font().italic(), false);
669 QString componentStr = "import QtQuick 2.0\nTextEdit { font.italic: true; text: \"Hello World\" }";
670 QDeclarativeComponent texteditComponent(&engine);
671 texteditComponent.setData(componentStr.toLatin1(), QUrl());
672 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
674 QVERIFY(textEditObject != 0);
675 QCOMPARE(textEditObject->font().italic(), true);
676 QCOMPARE(textEditObject->font().bold(), false);
680 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"Helvetica\"; text: \"Hello World\" }";
681 QDeclarativeComponent texteditComponent(&engine);
682 texteditComponent.setData(componentStr.toLatin1(), QUrl());
683 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
685 QVERIFY(textEditObject != 0);
686 QCOMPARE(textEditObject->font().family(), QString("Helvetica"));
687 QCOMPARE(textEditObject->font().bold(), false);
688 QCOMPARE(textEditObject->font().italic(), false);
692 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"\"; text: \"Hello World\" }";
693 QDeclarativeComponent texteditComponent(&engine);
694 texteditComponent.setData(componentStr.toLatin1(), QUrl());
695 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
697 QVERIFY(textEditObject != 0);
698 QCOMPARE(textEditObject->font().family(), QString(""));
702 void tst_qsgtextedit::color()
706 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello World\" }";
707 QDeclarativeComponent texteditComponent(&engine);
708 texteditComponent.setData(componentStr.toLatin1(), QUrl());
709 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
711 QSGTextEditPrivate *textEditPrivate = static_cast<QSGTextEditPrivate*>(QSGItemPrivate::get(textEditObject));
713 QVERIFY(textEditObject);
714 QVERIFY(textEditPrivate);
715 QVERIFY(textEditPrivate->control);
717 QPalette pal = textEditPrivate->control->palette();
718 QCOMPARE(textEditPrivate->color, QColor("black"));
719 QCOMPARE(textEditPrivate->color, pal.color(QPalette::Text));
722 for (int i = 0; i < colorStrings.size(); i++)
724 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
725 QDeclarativeComponent texteditComponent(&engine);
726 texteditComponent.setData(componentStr.toLatin1(), QUrl());
727 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
728 //qDebug() << "textEditObject: " << textEditObject->color() << "vs. " << QColor(colorStrings.at(i));
729 QVERIFY(textEditObject != 0);
730 QCOMPARE(textEditObject->color(), QColor(colorStrings.at(i)));
734 for (int i = 0; i < colorStrings.size(); i++)
736 QString componentStr = "import QtQuick 2.0\nTextEdit { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
737 QDeclarativeComponent texteditComponent(&engine);
738 texteditComponent.setData(componentStr.toLatin1(), QUrl());
739 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
740 QVERIFY(textEditObject != 0);
741 QCOMPARE(textEditObject->selectionColor(), QColor(colorStrings.at(i)));
745 for (int i = 0; i < colorStrings.size(); i++)
747 QString componentStr = "import QtQuick 2.0\nTextEdit { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
748 QDeclarativeComponent texteditComponent(&engine);
749 texteditComponent.setData(componentStr.toLatin1(), QUrl());
750 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
751 QVERIFY(textEditObject != 0);
752 QCOMPARE(textEditObject->selectedTextColor(), QColor(colorStrings.at(i)));
756 QString colorStr = "#AA001234";
757 QColor testColor("#001234");
758 testColor.setAlpha(170);
760 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStr + "\"; text: \"Hello World\" }";
761 QDeclarativeComponent texteditComponent(&engine);
762 texteditComponent.setData(componentStr.toLatin1(), QUrl());
763 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
765 QVERIFY(textEditObject != 0);
766 QCOMPARE(textEditObject->color(), testColor);
770 void tst_qsgtextedit::textMargin()
772 for (qreal i=0; i<=10; i+=0.3) {
773 QString componentStr = "import QtQuick 2.0\nTextEdit { textMargin: " + QString::number(i) + "; text: \"Hello World\" }";
774 QDeclarativeComponent texteditComponent(&engine);
775 texteditComponent.setData(componentStr.toLatin1(), QUrl());
776 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
777 QVERIFY(textEditObject != 0);
778 QCOMPARE(textEditObject->textMargin(), i);
782 void tst_qsgtextedit::persistentSelection()
785 QString componentStr = "import QtQuick 2.0\nTextEdit { persistentSelection: true; text: \"Hello World\" }";
786 QDeclarativeComponent texteditComponent(&engine);
787 texteditComponent.setData(componentStr.toLatin1(), QUrl());
788 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
789 QVERIFY(textEditObject != 0);
790 QCOMPARE(textEditObject->persistentSelection(), true);
794 QString componentStr = "import QtQuick 2.0\nTextEdit { persistentSelection: false; text: \"Hello World\" }";
795 QDeclarativeComponent texteditComponent(&engine);
796 texteditComponent.setData(componentStr.toLatin1(), QUrl());
797 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
798 QVERIFY(textEditObject != 0);
799 QCOMPARE(textEditObject->persistentSelection(), false);
803 void tst_qsgtextedit::focusOnPress()
806 QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: true; text: \"Hello World\" }";
807 QDeclarativeComponent texteditComponent(&engine);
808 texteditComponent.setData(componentStr.toLatin1(), QUrl());
809 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
810 QVERIFY(textEditObject != 0);
811 QCOMPARE(textEditObject->focusOnPress(), true);
815 QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: false; text: \"Hello World\" }";
816 QDeclarativeComponent texteditComponent(&engine);
817 texteditComponent.setData(componentStr.toLatin1(), QUrl());
818 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
819 QVERIFY(textEditObject != 0);
820 QCOMPARE(textEditObject->focusOnPress(), false);
824 void tst_qsgtextedit::selection()
826 QString testStr = standard[0];//TODO: What should happen for multiline/rich text?
827 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
828 QDeclarativeComponent texteditComponent(&engine);
829 texteditComponent.setData(componentStr.toLatin1(), QUrl());
830 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
831 QVERIFY(textEditObject != 0);
834 //Test selection follows cursor
835 for (int i=0; i<= testStr.size(); i++) {
836 textEditObject->setCursorPosition(i);
837 QCOMPARE(textEditObject->cursorPosition(), i);
838 QCOMPARE(textEditObject->selectionStart(), i);
839 QCOMPARE(textEditObject->selectionEnd(), i);
840 QVERIFY(textEditObject->selectedText().isNull());
843 textEditObject->setCursorPosition(0);
844 QVERIFY(textEditObject->cursorPosition() == 0);
845 QVERIFY(textEditObject->selectionStart() == 0);
846 QVERIFY(textEditObject->selectionEnd() == 0);
847 QVERIFY(textEditObject->selectedText().isNull());
849 // Verify invalid positions are ignored.
850 textEditObject->setCursorPosition(-1);
851 QVERIFY(textEditObject->cursorPosition() == 0);
852 QVERIFY(textEditObject->selectionStart() == 0);
853 QVERIFY(textEditObject->selectionEnd() == 0);
854 QVERIFY(textEditObject->selectedText().isNull());
856 textEditObject->setCursorPosition(textEditObject->text().count()+1);
857 QVERIFY(textEditObject->cursorPosition() == 0);
858 QVERIFY(textEditObject->selectionStart() == 0);
859 QVERIFY(textEditObject->selectionEnd() == 0);
860 QVERIFY(textEditObject->selectedText().isNull());
863 for (int i=0; i<= testStr.size(); i++) {
864 textEditObject->select(0,i);
865 QCOMPARE(testStr.mid(0,i), textEditObject->selectedText());
867 for (int i=0; i<= testStr.size(); i++) {
868 textEditObject->select(i,testStr.size());
869 QCOMPARE(testStr.mid(i,testStr.size()-i), textEditObject->selectedText());
872 textEditObject->setCursorPosition(0);
873 QVERIFY(textEditObject->cursorPosition() == 0);
874 QVERIFY(textEditObject->selectionStart() == 0);
875 QVERIFY(textEditObject->selectionEnd() == 0);
876 QVERIFY(textEditObject->selectedText().isNull());
878 //Test Error Ignoring behaviour
879 textEditObject->setCursorPosition(0);
880 QVERIFY(textEditObject->selectedText().isNull());
881 textEditObject->select(-10,0);
882 QVERIFY(textEditObject->selectedText().isNull());
883 textEditObject->select(100,101);
884 QVERIFY(textEditObject->selectedText().isNull());
885 textEditObject->select(0,-10);
886 QVERIFY(textEditObject->selectedText().isNull());
887 textEditObject->select(0,100);
888 QVERIFY(textEditObject->selectedText().isNull());
889 textEditObject->select(0,10);
890 QVERIFY(textEditObject->selectedText().size() == 10);
891 textEditObject->select(-10,0);
892 QVERIFY(textEditObject->selectedText().size() == 10);
893 textEditObject->select(100,101);
894 QVERIFY(textEditObject->selectedText().size() == 10);
895 textEditObject->select(0,-10);
896 QVERIFY(textEditObject->selectedText().size() == 10);
897 textEditObject->select(0,100);
898 QVERIFY(textEditObject->selectedText().size() == 10);
900 textEditObject->deselect();
901 QVERIFY(textEditObject->selectedText().isNull());
902 textEditObject->select(0,10);
903 QVERIFY(textEditObject->selectedText().size() == 10);
904 textEditObject->deselect();
905 QVERIFY(textEditObject->selectedText().isNull());
908 void tst_qsgtextedit::isRightToLeft_data()
910 QTest::addColumn<QString>("text");
911 QTest::addColumn<bool>("emptyString");
912 QTest::addColumn<bool>("firstCharacter");
913 QTest::addColumn<bool>("lastCharacter");
914 QTest::addColumn<bool>("middleCharacter");
915 QTest::addColumn<bool>("startString");
916 QTest::addColumn<bool>("midString");
917 QTest::addColumn<bool>("endString");
919 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
920 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
921 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
922 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
923 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
924 QTest::newRow("Bidi RTL + LTR + RTL") << QString::fromUtf16(arabic_str, 11) + QString("Hello world") + QString::fromUtf16(arabic_str, 11) << false << true << true << false << true << true << true;
925 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
928 void tst_qsgtextedit::isRightToLeft()
930 QFETCH(QString, text);
931 QFETCH(bool, emptyString);
932 QFETCH(bool, firstCharacter);
933 QFETCH(bool, lastCharacter);
934 QFETCH(bool, middleCharacter);
935 QFETCH(bool, startString);
936 QFETCH(bool, midString);
937 QFETCH(bool, endString);
939 QSGTextEdit textEdit;
940 textEdit.setText(text);
942 // first test that the right string is delivered to the QString::isRightToLeft()
943 QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
944 QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
945 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
946 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
947 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
948 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
950 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
951 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
953 // then test that the feature actually works
954 QCOMPARE(textEdit.isRightToLeft(0,0), emptyString);
955 QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter);
956 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
957 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
958 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString);
959 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString);
961 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
962 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString);
965 void tst_qsgtextedit::keySelection()
967 QSGView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
969 canvas.requestActivateWindow();
970 QTest::qWaitForWindowShown(&canvas);
971 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
972 canvas.requestActivateWindow();
974 QVERIFY(canvas.rootObject() != 0);
976 QSGTextEdit *input = qobject_cast<QSGTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
979 QTRY_VERIFY(input->hasActiveFocus() == true);
981 QSignalSpy spy(input, SIGNAL(selectionChanged()));
983 simulateKey(&canvas, Qt::Key_Right, Qt::ShiftModifier);
984 QVERIFY(input->hasActiveFocus() == true);
985 QCOMPARE(input->selectedText(), QString("a"));
986 QCOMPARE(spy.count(), 1);
987 simulateKey(&canvas, Qt::Key_Right);
988 QVERIFY(input->hasActiveFocus() == true);
989 QCOMPARE(input->selectedText(), QString());
990 QCOMPARE(spy.count(), 2);
991 simulateKey(&canvas, Qt::Key_Right);
992 QVERIFY(input->hasActiveFocus() == false);
993 QCOMPARE(input->selectedText(), QString());
994 QCOMPARE(spy.count(), 2);
996 simulateKey(&canvas, Qt::Key_Left);
997 QVERIFY(input->hasActiveFocus() == true);
998 QCOMPARE(spy.count(), 2);
999 simulateKey(&canvas, Qt::Key_Left, Qt::ShiftModifier);
1000 QVERIFY(input->hasActiveFocus() == true);
1001 QCOMPARE(input->selectedText(), QString("a"));
1002 QCOMPARE(spy.count(), 3);
1003 simulateKey(&canvas, Qt::Key_Left);
1004 QVERIFY(input->hasActiveFocus() == true);
1005 QCOMPARE(input->selectedText(), QString());
1006 QCOMPARE(spy.count(), 4);
1007 simulateKey(&canvas, Qt::Key_Left);
1008 QVERIFY(input->hasActiveFocus() == false);
1009 QCOMPARE(input->selectedText(), QString());
1010 QCOMPARE(spy.count(), 4);
1013 void tst_qsgtextedit::moveCursorSelection_data()
1015 QTest::addColumn<QString>("testStr");
1016 QTest::addColumn<int>("cursorPosition");
1017 QTest::addColumn<int>("movePosition");
1018 QTest::addColumn<QSGTextEdit::SelectionMode>("mode");
1019 QTest::addColumn<int>("selectionStart");
1020 QTest::addColumn<int>("selectionEnd");
1021 QTest::addColumn<bool>("reversible");
1023 QTest::newRow("(t)he|characters")
1024 << standard[0] << 0 << 1 << QSGTextEdit::SelectCharacters << 0 << 1 << true;
1025 QTest::newRow("do(g)|characters")
1026 << standard[0] << 43 << 44 << QSGTextEdit::SelectCharacters << 43 << 44 << true;
1027 QTest::newRow("jum(p)ed|characters")
1028 << standard[0] << 23 << 24 << QSGTextEdit::SelectCharacters << 23 << 24 << true;
1029 QTest::newRow("jumped( )over|characters")
1030 << standard[0] << 26 << 27 << QSGTextEdit::SelectCharacters << 26 << 27 << true;
1031 QTest::newRow("(the )|characters")
1032 << standard[0] << 0 << 4 << QSGTextEdit::SelectCharacters << 0 << 4 << true;
1033 QTest::newRow("( dog)|characters")
1034 << standard[0] << 40 << 44 << QSGTextEdit::SelectCharacters << 40 << 44 << true;
1035 QTest::newRow("( jumped )|characters")
1036 << standard[0] << 19 << 27 << QSGTextEdit::SelectCharacters << 19 << 27 << true;
1037 QTest::newRow("th(e qu)ick|characters")
1038 << standard[0] << 2 << 6 << QSGTextEdit::SelectCharacters << 2 << 6 << true;
1039 QTest::newRow("la(zy d)og|characters")
1040 << standard[0] << 38 << 42 << QSGTextEdit::SelectCharacters << 38 << 42 << true;
1041 QTest::newRow("jum(ped ov)er|characters")
1042 << standard[0] << 23 << 29 << QSGTextEdit::SelectCharacters << 23 << 29 << true;
1043 QTest::newRow("()the|characters")
1044 << standard[0] << 0 << 0 << QSGTextEdit::SelectCharacters << 0 << 0 << true;
1045 QTest::newRow("dog()|characters")
1046 << standard[0] << 44 << 44 << QSGTextEdit::SelectCharacters << 44 << 44 << true;
1047 QTest::newRow("jum()ped|characters")
1048 << standard[0] << 23 << 23 << QSGTextEdit::SelectCharacters << 23 << 23 << true;
1050 QTest::newRow("<(t)he>|words")
1051 << standard[0] << 0 << 1 << QSGTextEdit::SelectWords << 0 << 3 << true;
1052 QTest::newRow("<do(g)>|words")
1053 << standard[0] << 43 << 44 << QSGTextEdit::SelectWords << 41 << 44 << true;
1054 QTest::newRow("<jum(p)ed>|words")
1055 << standard[0] << 23 << 24 << QSGTextEdit::SelectWords << 20 << 26 << true;
1056 QTest::newRow("<jumped( )>over|words")
1057 << standard[0] << 26 << 27 << QSGTextEdit::SelectWords << 20 << 27 << false;
1058 QTest::newRow("jumped<( )over>|words,reversed")
1059 << standard[0] << 27 << 26 << QSGTextEdit::SelectWords << 26 << 31 << false;
1060 QTest::newRow("<(the )>quick|words")
1061 << standard[0] << 0 << 4 << QSGTextEdit::SelectWords << 0 << 4 << false;
1062 QTest::newRow("<(the )quick>|words,reversed")
1063 << standard[0] << 4 << 0 << QSGTextEdit::SelectWords << 0 << 9 << false;
1064 QTest::newRow("<lazy( dog)>|words")
1065 << standard[0] << 40 << 44 << QSGTextEdit::SelectWords << 36 << 44 << false;
1066 QTest::newRow("lazy<( dog)>|words,reversed")
1067 << standard[0] << 44 << 40 << QSGTextEdit::SelectWords << 40 << 44 << false;
1068 QTest::newRow("<fox( jumped )>over|words")
1069 << standard[0] << 19 << 27 << QSGTextEdit::SelectWords << 16 << 27 << false;
1070 QTest::newRow("fox<( jumped )over>|words,reversed")
1071 << standard[0] << 27 << 19 << QSGTextEdit::SelectWords << 19 << 31 << false;
1072 QTest::newRow("<th(e qu)ick>|words")
1073 << standard[0] << 2 << 6 << QSGTextEdit::SelectWords << 0 << 9 << true;
1074 QTest::newRow("<la(zy d)og|words>")
1075 << standard[0] << 38 << 42 << QSGTextEdit::SelectWords << 36 << 44 << true;
1076 QTest::newRow("<jum(ped ov)er>|words")
1077 << standard[0] << 23 << 29 << QSGTextEdit::SelectWords << 20 << 31 << true;
1078 QTest::newRow("<()>the|words")
1079 << standard[0] << 0 << 0 << QSGTextEdit::SelectWords << 0 << 0 << true;
1080 QTest::newRow("dog<()>|words")
1081 << standard[0] << 44 << 44 << QSGTextEdit::SelectWords << 44 << 44 << true;
1082 QTest::newRow("jum<()>ped|words")
1083 << standard[0] << 23 << 23 << QSGTextEdit::SelectWords << 23 << 23 << true;
1085 QTest::newRow("Hello<(,)> |words")
1086 << standard[2] << 5 << 6 << QSGTextEdit::SelectWords << 5 << 6 << true;
1087 QTest::newRow("Hello<(, )>world|words")
1088 << standard[2] << 5 << 7 << QSGTextEdit::SelectWords << 5 << 7 << false;
1089 QTest::newRow("Hello<(, )world>|words,reversed")
1090 << standard[2] << 7 << 5 << QSGTextEdit::SelectWords << 5 << 12 << false;
1091 QTest::newRow("<Hel(lo, )>world|words")
1092 << standard[2] << 3 << 7 << QSGTextEdit::SelectWords << 0 << 7 << false;
1093 QTest::newRow("<Hel(lo, )world>|words,reversed")
1094 << standard[2] << 7 << 3 << QSGTextEdit::SelectWords << 0 << 12 << false;
1095 QTest::newRow("<Hel(lo)>,|words")
1096 << standard[2] << 3 << 5 << QSGTextEdit::SelectWords << 0 << 5 << true;
1097 QTest::newRow("Hello<()>,|words")
1098 << standard[2] << 5 << 5 << QSGTextEdit::SelectWords << 5 << 5 << true;
1099 QTest::newRow("Hello,<()>|words")
1100 << standard[2] << 6 << 6 << QSGTextEdit::SelectWords << 6 << 6 << true;
1101 QTest::newRow("Hello<,( )>world|words")
1102 << standard[2] << 6 << 7 << QSGTextEdit::SelectWords << 5 << 7 << false;
1103 QTest::newRow("Hello,<( )world>|words,reversed")
1104 << standard[2] << 7 << 6 << QSGTextEdit::SelectWords << 6 << 12 << false;
1105 QTest::newRow("Hello<,( world)>|words")
1106 << standard[2] << 6 << 12 << QSGTextEdit::SelectWords << 5 << 12 << false;
1107 QTest::newRow("Hello,<( world)>|words,reversed")
1108 << standard[2] << 12 << 6 << QSGTextEdit::SelectWords << 6 << 12 << false;
1109 QTest::newRow("Hello<,( world!)>|words")
1110 << standard[2] << 6 << 13 << QSGTextEdit::SelectWords << 5 << 13 << false;
1111 QTest::newRow("Hello,<( world!)>|words,reversed")
1112 << standard[2] << 13 << 6 << QSGTextEdit::SelectWords << 6 << 13 << false;
1113 QTest::newRow("Hello<(, world!)>|words")
1114 << standard[2] << 5 << 13 << QSGTextEdit::SelectWords << 5 << 13 << true;
1115 QTest::newRow("world<(!)>|words")
1116 << standard[2] << 12 << 13 << QSGTextEdit::SelectWords << 12 << 13 << true;
1117 QTest::newRow("world!<()>)|words")
1118 << standard[2] << 13 << 13 << QSGTextEdit::SelectWords << 13 << 13 << true;
1119 QTest::newRow("world<()>!)|words")
1120 << standard[2] << 12 << 12 << QSGTextEdit::SelectWords << 12 << 12 << true;
1122 QTest::newRow("<(,)>olleH |words")
1123 << standard[3] << 7 << 8 << QSGTextEdit::SelectWords << 7 << 8 << true;
1124 QTest::newRow("<dlrow( ,)>olleH|words")
1125 << standard[3] << 6 << 8 << QSGTextEdit::SelectWords << 1 << 8 << false;
1126 QTest::newRow("dlrow<( ,)>olleH|words,reversed")
1127 << standard[3] << 8 << 6 << QSGTextEdit::SelectWords << 6 << 8 << false;
1128 QTest::newRow("<dlrow( ,ol)leH>|words")
1129 << standard[3] << 6 << 10 << QSGTextEdit::SelectWords << 1 << 13 << false;
1130 QTest::newRow("dlrow<( ,ol)leH>|words,reversed")
1131 << standard[3] << 10 << 6 << QSGTextEdit::SelectWords << 6 << 13 << false;
1132 QTest::newRow(",<(ol)leH>,|words")
1133 << standard[3] << 8 << 10 << QSGTextEdit::SelectWords << 8 << 13 << true;
1134 QTest::newRow(",<()>olleH|words")
1135 << standard[3] << 8 << 8 << QSGTextEdit::SelectWords << 8 << 8 << true;
1136 QTest::newRow("<()>,olleH|words")
1137 << standard[3] << 7 << 7 << QSGTextEdit::SelectWords << 7 << 7 << true;
1138 QTest::newRow("<dlrow( )>,olleH|words")
1139 << standard[3] << 6 << 7 << QSGTextEdit::SelectWords << 1 << 7 << false;
1140 QTest::newRow("dlrow<( ),>olleH|words,reversed")
1141 << standard[3] << 7 << 6 << QSGTextEdit::SelectWords << 6 << 8 << false;
1142 QTest::newRow("<(dlrow )>,olleH|words")
1143 << standard[3] << 1 << 7 << QSGTextEdit::SelectWords << 1 << 7 << false;
1144 QTest::newRow("<(dlrow ),>olleH|words,reversed")
1145 << standard[3] << 7 << 1 << QSGTextEdit::SelectWords << 1 << 8 << false;
1146 QTest::newRow("<(!dlrow )>,olleH|words")
1147 << standard[3] << 0 << 7 << QSGTextEdit::SelectWords << 0 << 7 << false;
1148 QTest::newRow("<(!dlrow ),>olleH|words,reversed")
1149 << standard[3] << 7 << 0 << QSGTextEdit::SelectWords << 0 << 8 << false;
1150 QTest::newRow("(!dlrow ,)olleH|words")
1151 << standard[3] << 0 << 8 << QSGTextEdit::SelectWords << 0 << 8 << true;
1152 QTest::newRow("<(!)>dlrow|words")
1153 << standard[3] << 0 << 1 << QSGTextEdit::SelectWords << 0 << 1 << true;
1154 QTest::newRow("<()>!dlrow|words")
1155 << standard[3] << 0 << 0 << QSGTextEdit::SelectWords << 0 << 0 << true;
1156 QTest::newRow("!<()>dlrow|words")
1157 << standard[3] << 1 << 1 << QSGTextEdit::SelectWords << 1 << 1 << true;
1160 void tst_qsgtextedit::moveCursorSelection()
1162 QFETCH(QString, testStr);
1163 QFETCH(int, cursorPosition);
1164 QFETCH(int, movePosition);
1165 QFETCH(QSGTextEdit::SelectionMode, mode);
1166 QFETCH(int, selectionStart);
1167 QFETCH(int, selectionEnd);
1168 QFETCH(bool, reversible);
1170 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1171 QDeclarativeComponent textinputComponent(&engine);
1172 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1173 QSGTextEdit *texteditObject = qobject_cast<QSGTextEdit*>(textinputComponent.create());
1174 QVERIFY(texteditObject != 0);
1176 texteditObject->setCursorPosition(cursorPosition);
1177 texteditObject->moveCursorSelection(movePosition, mode);
1179 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1180 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1181 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1184 texteditObject->setCursorPosition(movePosition);
1185 texteditObject->moveCursorSelection(cursorPosition, mode);
1187 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1188 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1189 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1193 void tst_qsgtextedit::moveCursorSelectionSequence_data()
1195 QTest::addColumn<QString>("testStr");
1196 QTest::addColumn<int>("cursorPosition");
1197 QTest::addColumn<int>("movePosition1");
1198 QTest::addColumn<int>("movePosition2");
1199 QTest::addColumn<int>("selection1Start");
1200 QTest::addColumn<int>("selection1End");
1201 QTest::addColumn<int>("selection2Start");
1202 QTest::addColumn<int>("selection2End");
1204 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
1209 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
1214 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
1219 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
1224 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1229 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1234 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1239 QTest::newRow("the quick<(^ {^bro)wn>} fox|rtl")
1244 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1249 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1254 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1259 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1264 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1269 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1274 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1279 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1284 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1289 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1295 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1300 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1306 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1311 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1318 void tst_qsgtextedit::moveCursorSelectionSequence()
1320 QFETCH(QString, testStr);
1321 QFETCH(int, cursorPosition);
1322 QFETCH(int, movePosition1);
1323 QFETCH(int, movePosition2);
1324 QFETCH(int, selection1Start);
1325 QFETCH(int, selection1End);
1326 QFETCH(int, selection2Start);
1327 QFETCH(int, selection2End);
1329 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1330 QDeclarativeComponent texteditComponent(&engine);
1331 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1332 QSGTextEdit *texteditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
1333 QVERIFY(texteditObject != 0);
1335 texteditObject->setCursorPosition(cursorPosition);
1337 texteditObject->moveCursorSelection(movePosition1, QSGTextEdit::SelectWords);
1338 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1339 QCOMPARE(texteditObject->selectionStart(), selection1Start);
1340 QCOMPARE(texteditObject->selectionEnd(), selection1End);
1342 texteditObject->moveCursorSelection(movePosition2, QSGTextEdit::SelectWords);
1343 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1344 QCOMPARE(texteditObject->selectionStart(), selection2Start);
1345 QCOMPARE(texteditObject->selectionEnd(), selection2End);
1349 void tst_qsgtextedit::mouseSelection_data()
1351 QTest::addColumn<QString>("qmlfile");
1352 QTest::addColumn<int>("from");
1353 QTest::addColumn<int>("to");
1354 QTest::addColumn<QString>("selectedText");
1357 QTest::newRow("on") << TESTDATA("mouseselection_true.qml") << 4 << 9 << "45678";
1358 QTest::newRow("off") << TESTDATA("mouseselection_false.qml") << 4 << 9 << QString();
1359 QTest::newRow("default") << TESTDATA("mouseselection_default.qml") << 4 << 9 << QString();
1360 QTest::newRow("off word selection") << TESTDATA("mouseselection_false_words.qml") << 4 << 9 << QString();
1361 QTest::newRow("on word selection (4,9)") << TESTDATA("mouseselection_true_words.qml") << 4 << 9 << "0123456789";
1362 QTest::newRow("on word selection (2,13)") << TESTDATA("mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1363 QTest::newRow("on word selection (2,30)") << TESTDATA("mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1364 QTest::newRow("on word selection (9,13)") << TESTDATA("mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1365 QTest::newRow("on word selection (9,30)") << TESTDATA("mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1366 QTest::newRow("on word selection (13,2)") << TESTDATA("mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1367 QTest::newRow("on word selection (20,2)") << TESTDATA("mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1368 QTest::newRow("on word selection (12,9)") << TESTDATA("mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1369 QTest::newRow("on word selection (30,9)") << TESTDATA("mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1372 void tst_qsgtextedit::mouseSelection()
1374 QFETCH(QString, qmlfile);
1377 QFETCH(QString, selectedText);
1379 QSGView canvas(QUrl::fromLocalFile(qmlfile));
1382 canvas.requestActivateWindow();
1383 QTest::qWaitForWindowShown(&canvas);
1384 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1386 QVERIFY(canvas.rootObject() != 0);
1387 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1388 QVERIFY(textEditObject != 0);
1390 // press-and-drag-and-release from x1 to x2
1391 QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint();
1392 QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint();
1393 QTest::mousePress(&canvas, Qt::LeftButton, 0, p1);
1394 QTest::mouseMove(&canvas, p2);
1395 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, p2);
1397 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1399 // Clicking and shift to clicking between the same points should select the same text.
1400 textEditObject->setCursorPosition(0);
1401 QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1402 QTest::mouseClick(&canvas, Qt::LeftButton, Qt::ShiftModifier, p2);
1404 if (!selectedText.isEmpty())
1405 QEXPECT_FAIL("", "QTBUG-21743", Continue);
1406 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1409 void tst_qsgtextedit::dragMouseSelection()
1411 QString qmlfile = TESTDATA("mouseselection_true.qml");
1413 QSGView canvas(QUrl::fromLocalFile(qmlfile));
1416 canvas.requestActivateWindow();
1417 QTest::qWaitForWindowShown(&canvas);
1418 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1420 QVERIFY(canvas.rootObject() != 0);
1421 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1422 QVERIFY(textEditObject != 0);
1424 // press-and-drag-and-release from x1 to x2
1427 int y = textEditObject->height()/2;
1428 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1429 QTest::mouseMove(&canvas, QPoint(x2, y));
1430 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1433 QTRY_VERIFY((str1 = textEditObject->selectedText()).length() > 3);
1435 // press and drag the current selection.
1438 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1439 QTest::mouseMove(&canvas, QPoint(x2, y));
1440 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1443 QTRY_VERIFY((str2 = textEditObject->selectedText()).length() > 3);
1445 QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and not the first moved.
1448 void tst_qsgtextedit::mouseSelectionMode_data()
1450 QTest::addColumn<QString>("qmlfile");
1451 QTest::addColumn<bool>("selectWords");
1454 QTest::newRow("SelectWords") << TESTDATA("mouseselectionmode_words.qml") << true;
1455 QTest::newRow("SelectCharacters") << TESTDATA("mouseselectionmode_characters.qml") << false;
1456 QTest::newRow("default") << TESTDATA("mouseselectionmode_default.qml") << false;
1459 void tst_qsgtextedit::mouseSelectionMode()
1461 QFETCH(QString, qmlfile);
1462 QFETCH(bool, selectWords);
1464 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1466 QSGView canvas(QUrl::fromLocalFile(qmlfile));
1469 canvas.requestActivateWindow();
1470 QTest::qWaitForWindowShown(&canvas);
1471 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1473 QVERIFY(canvas.rootObject() != 0);
1474 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1475 QVERIFY(textEditObject != 0);
1477 // press-and-drag-and-release from x1 to x2
1480 int y = textEditObject->height()/2;
1481 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1482 QTest::mouseMove(&canvas, QPoint(x2, y));
1483 //QTest::mouseMove(canvas, QPoint(x2,y)); // doesn't work
1484 // QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1485 // QGuiApplication::sendEvent(&canvas, &mv);
1486 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1487 QString str = textEditObject->selectedText();
1489 QTRY_COMPARE(textEditObject->selectedText(), text);
1491 QTRY_VERIFY(textEditObject->selectedText().length() > 3);
1492 QVERIFY(str != text);
1496 void tst_qsgtextedit::inputMethodHints()
1498 QSGView canvas(QUrl::fromLocalFile(TESTDATA("inputmethodhints.qml")));
1500 canvas.requestActivateWindow();
1502 QVERIFY(canvas.rootObject() != 0);
1503 QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1504 QVERIFY(textEditObject != 0);
1505 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText);
1506 textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
1507 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhUppercaseOnly);
1510 void tst_qsgtextedit::positionAt()
1512 QSGView canvas(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
1513 QVERIFY(canvas.rootObject() != 0);
1515 canvas.requestActivateWindow();
1516 canvas.requestActivateWindow();
1517 QTest::qWaitForWindowShown(&canvas);
1519 QSGTextEdit *texteditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1520 QVERIFY(texteditObject != 0);
1522 QFontMetrics fm(texteditObject->font());
1523 const int y0 = fm.height() / 2;
1524 const int y1 = fm.height() * 3 / 2;
1526 int pos = texteditObject->positionAt(texteditObject->width()/2, y0);
1528 if (!qmlDisableDistanceField()) {
1529 QTextLayout layout(texteditObject->text().left(pos));
1533 option.setUseDesignMetrics(true);
1534 layout.setTextOption(option);
1537 layout.beginLayout();
1538 QTextLine line = layout.createLine();
1541 width = ceil(line.horizontalAdvance());
1544 width = fm.width(texteditObject->text().left(pos));
1548 int diff = abs(int(width-texteditObject->width()/2));
1550 QEXPECT_FAIL("", "QTBUG-21689", Abort);
1551 // some tollerance for different fonts.
1558 const qreal x0 = texteditObject->positionToRectangle(pos).x();
1559 const qreal x1 = texteditObject->positionToRectangle(pos + 1).x();
1561 QString preeditText = texteditObject->text().mid(0, pos);
1562 texteditObject->setText(texteditObject->text().mid(pos));
1563 texteditObject->setCursorPosition(0);
1565 QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1566 QGuiApplication::sendEvent(&canvas, &inputEvent);
1568 // Check all points within the preedit text return the same position.
1569 QCOMPARE(texteditObject->positionAt(0, y0), 0);
1570 QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0);
1571 QCOMPARE(texteditObject->positionAt(x0, y0), 0);
1573 // Verify positioning returns to normal after the preedit text.
1574 QCOMPARE(texteditObject->positionAt(x1, y0), 1);
1575 QCOMPARE(texteditObject->positionToRectangle(1).x(), x1);
1577 QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0);
1580 void tst_qsgtextedit::cursorDelegate()
1582 QSGView view(QUrl::fromLocalFile(TESTDATA("cursorTest.qml")));
1584 view.requestActivateWindow();
1585 QSGTextEdit *textEditObject = view.rootObject()->findChild<QSGTextEdit*>("textEditObject");
1586 QVERIFY(textEditObject != 0);
1587 QVERIFY(textEditObject->findChild<QSGItem*>("cursorInstance"));
1588 //Test Delegate gets created
1589 textEditObject->setFocus(true);
1590 QSGItem* delegateObject = textEditObject->findChild<QSGItem*>("cursorInstance");
1591 QVERIFY(delegateObject);
1592 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1593 //Test Delegate gets moved
1594 for (int i=0; i<= textEditObject->text().length(); i++) {
1595 textEditObject->setCursorPosition(i);
1596 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1597 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1599 // Clear preedit text;
1600 QInputMethodEvent event;
1601 QGuiApplication::sendEvent(&view, &event);
1604 // Test delegate gets moved on mouse press.
1605 textEditObject->setSelectByMouse(true);
1606 textEditObject->setCursorPosition(0);
1607 const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint();
1608 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
1610 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1611 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1612 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1614 // Test delegate gets moved on mouse drag
1615 textEditObject->setCursorPosition(0);
1616 const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint();
1617 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
1618 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1619 QGuiApplication::sendEvent(&view, &mv);
1620 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
1622 QTRY_COMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1623 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1625 textEditObject->setReadOnly(true);
1626 textEditObject->setCursorPosition(0);
1627 QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
1629 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1630 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1631 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1633 textEditObject->setCursorPosition(0);
1634 QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
1636 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1637 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1638 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1640 textEditObject->setCursorPosition(0);
1641 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1642 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1643 //Test Delegate gets deleted
1644 textEditObject->setCursorDelegate(0);
1645 QVERIFY(!textEditObject->findChild<QSGItem*>("cursorInstance"));
1648 void tst_qsgtextedit::cursorVisible()
1650 QSGView view(QUrl::fromLocalFile(TESTDATA("cursorVisible.qml")));
1652 view.requestActivateWindow();
1653 QTest::qWaitForWindowShown(&view);
1654 QTRY_COMPARE(&view, qGuiApp->focusWindow());
1657 QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool)));
1659 QCOMPARE(edit.isCursorVisible(), false);
1661 edit.setCursorVisible(true);
1662 QCOMPARE(edit.isCursorVisible(), true);
1663 QCOMPARE(spy.count(), 1);
1665 edit.setCursorVisible(false);
1666 QCOMPARE(edit.isCursorVisible(), false);
1667 QCOMPARE(spy.count(), 2);
1669 edit.setFocus(true);
1670 QCOMPARE(edit.isCursorVisible(), false);
1671 QCOMPARE(spy.count(), 2);
1673 edit.setParentItem(view.rootObject());
1674 QCOMPARE(edit.isCursorVisible(), true);
1675 QCOMPARE(spy.count(), 3);
1677 edit.setFocus(false);
1678 QCOMPARE(edit.isCursorVisible(), false);
1679 QCOMPARE(spy.count(), 4);
1681 edit.setFocus(true);
1682 QCOMPARE(edit.isCursorVisible(), true);
1683 QCOMPARE(spy.count(), 5);
1685 QEXPECT_FAIL("", "Most likely a side-effect of QTBUG-21489", Abort);
1686 view.setWindowState(Qt::WindowNoState);
1687 QCOMPARE(edit.isCursorVisible(), false);
1688 QCOMPARE(spy.count(), 6);
1690 view.requestActivateWindow();
1691 QCOMPARE(edit.isCursorVisible(), true);
1692 QCOMPARE(spy.count(), 7);
1694 // on mac, setActiveWindow(0) on mac does not deactivate the current application
1695 // (you have to switch to a different app or hide the current app to trigger this)
1696 #if !defined(Q_WS_MAC)
1697 // on mac, setActiveWindow(0) on mac does not deactivate the current application
1698 // (you have to switch to a different app or hide the current app to trigger this)
1699 // QApplication::setActiveWindow(0);
1700 // QTRY_COMPARE(QApplication::focusWindow(), static_cast<QWidget *>(0));
1701 // QCOMPARE(edit.isCursorVisible(), false);
1702 // QCOMPARE(spy.count(), 8);
1704 // view.requestActivateWindow();
1705 // QTest::qWaitForWindowShown(&view);
1706 // QTRY_COMPARE(view.windowState(), Qt::WindowActive);
1707 // QCOMPARE(edit.isCursorVisible(), true);
1708 // QCOMPARE(spy.count(), 9);
1712 void tst_qsgtextedit::delegateLoading_data()
1714 QTest::addColumn<QString>("qmlfile");
1715 QTest::addColumn<QString>("error");
1718 QTest::newRow("pass") << "cursorHttpTestPass.qml" << "";
1719 QTest::newRow("fail1") << "cursorHttpTestFail1.qml" << "http://localhost:42332/FailItem.qml: Remote host closed the connection ";
1720 QTest::newRow("fail2") << "cursorHttpTestFail2.qml" << "http://localhost:42332/ErrItem.qml:4:5: Fungus is not a type ";
1723 void tst_qsgtextedit::delegateLoading()
1725 QFETCH(QString, qmlfile);
1726 QFETCH(QString, error);
1728 TestHTTPServer server(42332);
1729 server.serveDirectory(TESTDATA("httpfail"), TestHTTPServer::Disconnect);
1730 server.serveDirectory(TESTDATA("httpslow"), TestHTTPServer::Delay);
1731 server.serveDirectory(TESTDATA("http"));
1733 QSGView view(QUrl(QLatin1String("http://localhost:42332/") + qmlfile));
1735 view.requestActivateWindow();
1737 if (!error.isEmpty()) {
1738 QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
1739 QTRY_VERIFY(view.status()==QSGView::Error);
1740 QTRY_VERIFY(!view.rootObject()); // there is fail item inside this test
1742 QTRY_VERIFY(view.rootObject());//Wait for loading to finish.
1743 QSGTextEdit *textEditObject = view.rootObject()->findChild<QSGTextEdit*>("textEditObject");
1744 // view.rootObject()->dumpObjectTree();
1745 QVERIFY(textEditObject != 0);
1746 textEditObject->setFocus(true);
1748 delegate = view.rootObject()->findChild<QSGItem*>("delegateOkay");
1750 delegate = view.rootObject()->findChild<QSGItem*>("delegateSlow");
1757 //A test should be added here with a component which is ready but component.create() returns null
1758 //Not sure how to accomplish this with QSGTextEdits cursor delegate
1759 //###This was only needed for code coverage, and could be a case of overzealous defensive programming
1760 //delegate = view.rootObject()->findChild<QSGItem*>("delegateErrorB");
1761 //QVERIFY(!delegate);
1765 TextEdit element should only handle left/right keys until the cursor reaches
1766 the extent of the text, then they should ignore the keys.
1768 void tst_qsgtextedit::navigation()
1770 QSGView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1772 canvas.requestActivateWindow();
1774 QVERIFY(canvas.rootObject() != 0);
1776 QSGItem *input = qobject_cast<QSGItem *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1778 QVERIFY(input != 0);
1779 QTRY_VERIFY(input->hasActiveFocus() == true);
1780 simulateKey(&canvas, Qt::Key_Left);
1781 QVERIFY(input->hasActiveFocus() == false);
1782 simulateKey(&canvas, Qt::Key_Right);
1783 QVERIFY(input->hasActiveFocus() == true);
1784 simulateKey(&canvas, Qt::Key_Right);
1785 QVERIFY(input->hasActiveFocus() == true);
1786 simulateKey(&canvas, Qt::Key_Right);
1787 QVERIFY(input->hasActiveFocus() == false);
1788 simulateKey(&canvas, Qt::Key_Left);
1789 QVERIFY(input->hasActiveFocus() == true);
1792 void tst_qsgtextedit::copyAndPaste() {
1793 #ifndef QT_NO_CLIPBOARD
1797 PasteboardRef pasteboard;
1798 OSStatus status = PasteboardCreate(0, &pasteboard);
1799 if (status == noErr)
1800 CFRelease(pasteboard);
1802 QSKIP("This machine doesn't support the clipboard", SkipAll);
1806 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
1807 QDeclarativeComponent textEditComponent(&engine);
1808 textEditComponent.setData(componentStr.toLatin1(), QUrl());
1809 QSGTextEdit *textEdit = qobject_cast<QSGTextEdit*>(textEditComponent.create());
1810 QVERIFY(textEdit != 0);
1813 QCOMPARE(textEdit->text().length(), 12);
1814 textEdit->select(0, textEdit->text().length());;
1816 QCOMPARE(textEdit->selectedText(), QString("Hello world!"));
1817 QCOMPARE(textEdit->selectedText().length(), 12);
1818 textEdit->setCursorPosition(0);
1819 QVERIFY(textEdit->canPaste());
1821 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
1822 QCOMPARE(textEdit->text().length(), 24);
1825 QVERIFY(textEdit->canPaste());
1826 textEdit->setReadOnly(true);
1827 QVERIFY(!textEdit->canPaste());
1828 textEdit->setReadOnly(false);
1829 QVERIFY(textEdit->canPaste());
1832 // test that document and internal text attribute are in sync
1833 QSGItemPrivate* pri = QSGItemPrivate::get(textEdit);
1834 QSGTextEditPrivate *editPrivate = static_cast<QSGTextEditPrivate*>(pri);
1835 QCOMPARE(textEdit->text(), editPrivate->text);
1838 textEdit->setCursorPosition(0);
1839 textEdit->selectWord();
1840 QCOMPARE(textEdit->selectedText(), QString("Hello"));
1842 // select all and cut
1843 textEdit->selectAll();
1845 QCOMPARE(textEdit->text().length(), 0);
1847 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
1848 QCOMPARE(textEdit->text().length(), 24);
1852 void tst_qsgtextedit::canPaste() {
1853 #ifndef QT_NO_CLIPBOARD
1855 QGuiApplication::clipboard()->setText("Some text");
1857 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
1858 QDeclarativeComponent textEditComponent(&engine);
1859 textEditComponent.setData(componentStr.toLatin1(), QUrl());
1860 QSGTextEdit *textEdit = qobject_cast<QSGTextEdit*>(textEditComponent.create());
1861 QVERIFY(textEdit != 0);
1863 // check initial value - QTBUG-17765
1865 QCOMPARE(textEdit->canPaste(), tc.canPaste());
1870 void tst_qsgtextedit::canPasteEmpty() {
1871 #ifndef QT_NO_CLIPBOARD
1873 QGuiApplication::clipboard()->clear();
1875 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
1876 QDeclarativeComponent textEditComponent(&engine);
1877 textEditComponent.setData(componentStr.toLatin1(), QUrl());
1878 QSGTextEdit *textEdit = qobject_cast<QSGTextEdit*>(textEditComponent.create());
1879 QVERIFY(textEdit != 0);
1881 // check initial value - QTBUG-17765
1883 QCOMPARE(textEdit->canPaste(), tc.canPaste());
1888 void tst_qsgtextedit::readOnly()
1890 QSGView canvas(QUrl::fromLocalFile(TESTDATA("readOnly.qml")));
1892 canvas.requestActivateWindow();
1894 QVERIFY(canvas.rootObject() != 0);
1896 QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1899 QTRY_VERIFY(edit->hasActiveFocus() == true);
1900 QVERIFY(edit->isReadOnly() == true);
1901 QString initial = edit->text();
1902 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
1903 simulateKey(&canvas, k);
1904 simulateKey(&canvas, Qt::Key_Return);
1905 simulateKey(&canvas, Qt::Key_Space);
1906 simulateKey(&canvas, Qt::Key_Escape);
1907 QCOMPARE(edit->text(), initial);
1909 edit->setCursorPosition(3);
1910 edit->setReadOnly(false);
1911 QCOMPARE(edit->isReadOnly(), false);
1912 QCOMPARE(edit->cursorPosition(), edit->text().length());
1915 void tst_qsgtextedit::simulateKey(QSGView *view, int key, Qt::KeyboardModifiers modifiers)
1917 QKeyEvent press(QKeyEvent::KeyPress, key, modifiers);
1918 QKeyEvent release(QKeyEvent::KeyRelease, key, modifiers);
1920 QGuiApplication::sendEvent(view, &press);
1921 QGuiApplication::sendEvent(view, &release);
1926 class MyInputContext : public QInputContext
1929 MyInputContext() : updateReceived(false), eventType(QEvent::None) {}
1930 ~MyInputContext() {}
1932 QString identifierName() { return QString(); }
1933 QString language() { return QString(); }
1937 bool isComposing() const { return false; }
1939 void update() { updateReceived = true; }
1941 void sendPreeditText(const QString &text, int cursor)
1943 QList<QInputMethodEvent::Attribute> attributes;
1944 attributes.append(QInputMethodEvent::Attribute(
1945 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
1947 QInputMethodEvent event(text, attributes);
1951 void mouseHandler(int x, QMouseEvent *event)
1954 eventType = event->type();
1955 eventPosition = event->pos();
1956 eventGlobalPosition = event->globalPos();
1957 eventButton = event->button();
1958 eventButtons = event->buttons();
1959 eventModifiers = event->modifiers();
1962 bool updateReceived;
1964 QEvent::Type eventType;
1965 QPoint eventPosition;
1966 QPoint eventGlobalPosition;
1967 Qt::MouseButton eventButton;
1968 Qt::MouseButtons eventButtons;
1969 Qt::KeyboardModifiers eventModifiers;
1973 void tst_qsgtextedit::textInput()
1975 QSGView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml")));
1977 view.requestActivateWindow();
1978 QTest::qWaitForWindowShown(&view);
1979 QTRY_COMPARE(&view, qGuiApp->focusWindow());
1980 QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
1982 QVERIFY(edit->hasActiveFocus() == true);
1984 // test that input method event is committed
1985 QInputMethodEvent event;
1986 event.setCommitString( "Hello world!", 0, 0);
1987 QGuiApplication::sendEvent(&view, &event);
1988 QEXPECT_FAIL("", "QTBUG-21689", Abort);
1989 QCOMPARE(edit->text(), QString("Hello world!"));
1992 // test that document and internal text attribute are in sync
1993 QSGTextEditPrivate *editPrivate = static_cast<QSGTextEditPrivate*>(QSGItemPrivate::get(edit));
1994 QCOMPARE(editPrivate->text, QString("Hello world!"));
1997 void tst_qsgtextedit::openInputPanel()
1999 QSGView view(QUrl::fromLocalFile(TESTDATA("openInputPanel.qml")));
2001 view.requestActivateWindow();
2002 QTest::qWaitForWindowShown(&view);
2003 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2005 QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
2008 // check default values
2009 QVERIFY(edit->focusOnPress());
2010 QVERIFY(!edit->hasActiveFocus());
2011 qDebug() << &edit << qApp->inputPanel()->inputItem();
2012 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2013 QEXPECT_FAIL("", "QTBUG-21946", Abort);
2014 QCOMPARE(qApp->inputPanel()->visible(), false);
2016 // input panel should open on focus
2017 QPoint centerPoint(view.width()/2, view.height()/2);
2018 Qt::KeyboardModifiers noModifiers = 0;
2019 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2020 QGuiApplication::processEvents();
2021 QVERIFY(edit->hasActiveFocus());
2022 QCOMPARE(qApp->inputPanel()->inputItem(), edit);
2023 QCOMPARE(qApp->inputPanel()->visible(), true);
2024 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2026 // input panel should be re-opened when pressing already focused TextEdit
2027 qApp->inputPanel()->hide();
2028 QCOMPARE(qApp->inputPanel()->visible(), false);
2029 QVERIFY(edit->hasActiveFocus());
2030 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2031 QGuiApplication::processEvents();
2032 QCOMPARE(qApp->inputPanel()->visible(), true);
2033 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2035 // input panel should stay visible if focus is lost to another text editor
2036 QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2037 QSGTextEdit anotherEdit;
2038 anotherEdit.setParentItem(view.rootObject());
2039 anotherEdit.setFocus(true);
2040 QCOMPARE(qApp->inputPanel()->visible(), true);
2041 QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherEdit));
2042 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2044 anotherEdit.setFocus(false);
2045 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2046 QCOMPARE(view.activeFocusItem(), view.rootItem());
2047 anotherEdit.setFocus(true);
2049 // input item should be null if focus is lost to an item that doesn't accept inputs
2051 item.setParentItem(view.rootObject());
2052 item.setFocus(true);
2053 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2054 QCOMPARE(view.activeFocusItem(), &item);
2056 qApp->inputPanel()->hide();
2058 // input panel should not be opened if TextEdit is read only
2059 edit->setReadOnly(true);
2060 edit->setFocus(true);
2061 QCOMPARE(qApp->inputPanel()->visible(), false);
2062 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2063 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2064 QGuiApplication::processEvents();
2065 QCOMPARE(qApp->inputPanel()->visible(), false);
2067 // input panel should not be opened if focusOnPress is set to false
2068 edit->setFocusOnPress(false);
2069 edit->setFocus(false);
2070 edit->setFocus(true);
2071 QCOMPARE(qApp->inputPanel()->visible(), false);
2072 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2073 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2074 QCOMPARE(qApp->inputPanel()->visible(), false);
2076 // input panel should open when openSoftwareInputPanel is called
2077 edit->openSoftwareInputPanel();
2078 QCOMPARE(qApp->inputPanel()->visible(), true);
2080 // input panel should close when closeSoftwareInputPanel is called
2081 edit->closeSoftwareInputPanel();
2082 QCOMPARE(qApp->inputPanel()->visible(), false);
2085 void tst_qsgtextedit::geometrySignals()
2087 QDeclarativeComponent component(&engine, TESTDATA("geometrySignals.qml"));
2088 QObject *o = component.create();
2090 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2091 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2095 void tst_qsgtextedit::pastingRichText_QTBUG_14003()
2097 #ifndef QT_NO_CLIPBOARD
2098 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.PlainText }";
2099 QDeclarativeComponent component(&engine);
2100 component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2101 QSGTextEdit *obj = qobject_cast<QSGTextEdit*>(component.create());
2103 QTRY_VERIFY(obj != 0);
2104 QTRY_VERIFY(obj->textFormat() == QSGTextEdit::PlainText);
2106 QMimeData *mData = new QMimeData;
2107 mData->setHtml("<font color=\"red\">Hello</font>");
2108 QGuiApplication::clipboard()->setMimeData(mData);
2111 QTRY_VERIFY(obj->text() == "");
2112 QTRY_VERIFY(obj->textFormat() == QSGTextEdit::PlainText);
2116 void tst_qsgtextedit::implicitSize_data()
2118 QTest::addColumn<QString>("text");
2119 QTest::addColumn<QString>("wrap");
2120 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.NoWrap";
2121 QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.NoWrap";
2122 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.Wrap";
2123 QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.Wrap";
2126 void tst_qsgtextedit::implicitSize()
2128 QFETCH(QString, text);
2129 QFETCH(QString, wrap);
2130 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
2131 QDeclarativeComponent textComponent(&engine);
2132 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2133 QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
2135 QVERIFY(textObject->width() < textObject->implicitWidth());
2136 QVERIFY(textObject->height() == textObject->implicitHeight());
2138 textObject->resetWidth();
2139 QVERIFY(textObject->width() == textObject->implicitWidth());
2140 QVERIFY(textObject->height() == textObject->implicitHeight());
2143 void tst_qsgtextedit::testQtQuick11Attributes()
2145 QFETCH(QString, code);
2146 QFETCH(QString, warning);
2147 QFETCH(QString, error);
2149 QDeclarativeEngine engine;
2152 QDeclarativeComponent valid(&engine);
2153 valid.setData("import QtQuick 2.0; TextEdit { " + code.toUtf8() + " }", QUrl(""));
2154 obj = valid.create();
2156 QVERIFY(valid.errorString().isEmpty());
2159 QDeclarativeComponent invalid(&engine);
2160 invalid.setData("import QtQuick 1.0; TextEdit { " + code.toUtf8() + " }", QUrl(""));
2161 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2162 obj = invalid.create();
2163 QCOMPARE(invalid.errorString(), error);
2167 void tst_qsgtextedit::testQtQuick11Attributes_data()
2169 QTest::addColumn<QString>("code");
2170 QTest::addColumn<QString>("warning");
2171 QTest::addColumn<QString>("error");
2173 QTest::newRow("canPaste") << "property bool foo: canPaste"
2174 << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2177 QTest::newRow("lineCount") << "property int foo: lineCount"
2178 << "<Unknown File>:1: ReferenceError: Can't find variable: lineCount"
2181 QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2182 << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2185 QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2186 << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2189 QTest::newRow("onLinkActivated") << "onLinkActivated: {}"
2190 << "QDeclarativeComponent: Component is not ready"
2191 << ":1 \"TextEdit.onLinkActivated\" is not available in QtQuick 1.0.\n";
2194 void tst_qsgtextedit::preeditMicroFocus()
2197 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2200 QString preeditText = "super";
2202 QSGView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml")));
2204 view.requestActivateWindow();
2205 QTest::qWaitForWindowShown(&view);
2207 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2208 QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
2211 QSignalSpy cursorRectangleSpy(edit, SIGNAL(cursorRectangleChanged()));
2214 QRect previousRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2216 // Verify that the micro focus rect is positioned the same for position 0 as
2217 // it would be if there was no preedit text.
2218 ic.updateReceived = false;
2219 ic.sendPreeditText(preeditText, 0);
2220 currentRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2221 QCOMPARE(currentRect, previousRect);
2222 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2223 QCOMPARE(ic.updateReceived, false); // The cursor position hasn't changed.
2225 QCOMPARE(cursorRectangleSpy.count(), 0);
2227 // Verify that the micro focus rect moves to the left as the cursor position
2229 for (int i = 1; i <= 5; ++i) {
2230 ic.updateReceived = false;
2231 ic.sendPreeditText(preeditText, i);
2232 currentRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2233 QVERIFY(previousRect.left() < currentRect.left());
2234 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2235 QCOMPARE(ic.updateReceived, true);
2237 QVERIFY(cursorRectangleSpy.count() > 0);
2238 cursorRectangleSpy.clear();
2239 previousRect = currentRect;
2242 // Verify that if there is no preedit cursor then the micro focus rect is the
2243 // same as it would be if it were positioned at the end of the preedit text.
2244 ic.sendPreeditText(preeditText, 0);
2245 ic.updateReceived = false;
2246 ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2247 currentRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2248 QCOMPARE(currentRect, previousRect);
2249 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2250 QCOMPARE(ic.updateReceived, true);
2252 QVERIFY(cursorRectangleSpy.count() > 0);
2256 void tst_qsgtextedit::inputContextMouseHandler()
2260 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2263 QString text = "supercalifragisiticexpialidocious!";
2265 QSGView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2267 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
2268 // and QWidget won't allow an input context to be set when the flag is not set.
2269 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2270 view.setInputContext(&ic);
2271 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2273 view.requestActivateWindow();
2274 QTest::qWaitForWindowShown(&view);
2276 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2277 QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
2279 edit->setCursorPosition(12);
2281 QFontMetricsF fm(edit->font());
2282 const qreal y = fm.height() / 2;
2284 QPoint position2 = edit->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
2285 QPoint position8 = edit->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
2286 QPoint position20 = edit->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
2287 QPoint position27 = edit->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
2288 QPoint globalPosition2 = view.mapToGlobal(position2);
2289 QPoint globalposition8 = view.mapToGlobal(position8);
2290 QPoint globalposition20 = view.mapToGlobal(position20);
2291 QPoint globalposition27 = view.mapToGlobal(position27);
2293 ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2295 QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
2296 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2297 QCOMPARE(ic.eventPosition, position2);
2298 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2299 QCOMPARE(ic.eventButton, Qt::LeftButton);
2300 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2301 QVERIFY(ic.cursor < 0);
2302 ic.eventType = QEvent::None;
2304 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
2305 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2306 QCOMPARE(ic.eventPosition, position2);
2307 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2308 QCOMPARE(ic.eventButton, Qt::LeftButton);
2309 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2310 QVERIFY(ic.cursor < 0);
2311 ic.eventType = QEvent::None;
2313 { QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2314 QGuiApplication::sendEvent(&view, &mv); }
2315 QCOMPARE(ic.eventType, QEvent::None);
2317 { QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2318 QGuiApplication::sendEvent(&view, &mv); }
2319 QCOMPARE(ic.eventType, QEvent::MouseMove);
2320 QCOMPARE(ic.eventPosition, position27);
2321 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2322 QCOMPARE(ic.eventButton, Qt::LeftButton);
2323 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2324 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); // 15 is expected but some platforms may be off by one.
2325 ic.eventType = QEvent::None;
2327 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
2328 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2329 QCOMPARE(ic.eventPosition, position27);
2330 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2331 QCOMPARE(ic.eventButton, Qt::LeftButton);
2332 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2333 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2334 ic.eventType = QEvent::None;
2336 // And in the other direction.
2337 QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
2338 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2339 QCOMPARE(ic.eventPosition, position27);
2340 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2341 QCOMPARE(ic.eventButton, Qt::LeftButton);
2342 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2343 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2344 ic.eventType = QEvent::None;
2346 QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
2347 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2348 QCOMPARE(ic.eventPosition, position27);
2349 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2350 QCOMPARE(ic.eventButton, Qt::RightButton);
2351 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2352 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2353 ic.eventType = QEvent::None;
2355 { QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2356 QGuiApplication::sendEvent(&view, &mv); }
2357 QCOMPARE(ic.eventType, QEvent::MouseMove);
2358 QCOMPARE(ic.eventPosition, position20);
2359 QCOMPARE(ic.eventGlobalPosition, globalposition20);
2360 QCOMPARE(ic.eventButton, Qt::RightButton);
2361 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2362 QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2363 ic.eventType = QEvent::None;
2365 { QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2366 QGuiApplication::sendEvent(&view, &mv); }
2367 QCOMPARE(ic.eventType, QEvent::None);
2369 QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
2370 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2371 QCOMPARE(ic.eventPosition, position2);
2372 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2373 QCOMPARE(ic.eventButton, Qt::RightButton);
2374 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2375 QVERIFY(ic.cursor < 0);
2376 ic.eventType = QEvent::None;
2380 void tst_qsgtextedit::inputMethodComposing()
2382 QString text = "supercalifragisiticexpialidocious!";
2384 QSGView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2386 view.requestActivateWindow();
2387 QTest::qWaitForWindowShown(&view);
2388 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2389 QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
2391 QSignalSpy spy(edit, SIGNAL(inputMethodComposingChanged()));
2392 edit->setCursorPosition(12);
2394 QCOMPARE(edit->isInputMethodComposing(), false);
2397 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2398 QGuiApplication::sendEvent(edit, &event);
2401 QCOMPARE(edit->isInputMethodComposing(), true);
2402 QCOMPARE(spy.count(), 1);
2405 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2406 QGuiApplication::sendEvent(edit, &event);
2408 QCOMPARE(spy.count(), 1);
2411 QInputMethodEvent event;
2412 QGuiApplication::sendEvent(edit, &event);
2414 QCOMPARE(edit->isInputMethodComposing(), false);
2415 QCOMPARE(spy.count(), 2);
2418 void tst_qsgtextedit::cursorRectangleSize()
2420 QSGView *canvas = new QSGView(QUrl::fromLocalFile(TESTDATA("CursorRect.qml")));
2421 QVERIFY(canvas->rootObject() != 0);
2423 canvas->requestActivateWindow();
2424 QTest::qWaitForWindowShown(canvas);
2426 QSGTextEdit *textEdit = qobject_cast<QSGTextEdit *>(canvas->rootObject());
2427 QVERIFY(textEdit != 0);
2428 textEdit->setFocus(Qt::OtherFocusReason);
2429 QRectF cursorRect = textEdit->positionToRectangle(textEdit->cursorPosition());
2430 QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImCursorRectangle).toRectF();
2431 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2432 qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
2434 QRectF microFocusFromApp = event.value(Qt::ImCursorRectangle).toRectF();
2436 QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2437 QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2442 QTEST_MAIN(tst_qsgtextedit)
2444 #include "tst_qsgtextedit.moc"