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 <QtDeclarative/qdeclarativeengine.h>
46 #include <QtDeclarative/qsgview.h>
47 #include <private/qsgtextinput_p.h>
48 #include <private/qsgtextinput_p_p.h>
52 #include <QInputContext>
53 #include <QtWidgets/5.0.0/QtWidgets/private/qapplication_p.h>
54 #include <private/qsgdistancefieldglyphcache_p.h>
55 #include <QtOpenGL/QGLShaderProgram>
58 #include "qplatformdefs.h"
61 // In Symbian OS test data is located in applications private dir
65 Q_DECLARE_METATYPE(QSGTextInput::SelectionMode)
66 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
68 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
70 // XXX This will be replaced by some clever persistent platform image store.
71 QString persistent_dir = SRCDIR "/data";
72 QString arch = "unknown-architecture"; // QTest needs to help with this.
74 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
76 if (!QFile::exists(expectfile)) {
77 actual.save(expectfile);
78 qWarning() << "created" << expectfile;
84 class tst_qsgtextinput : public QObject
93 void cleanupTestCase();
99 void isRightToLeft_data();
100 void isRightToLeft();
101 void moveCursorSelection_data();
102 void moveCursorSelection();
103 void moveCursorSelectionSequence_data();
104 void moveCursorSelectionSequence();
105 void dragMouseSelection();
106 void mouseSelectionMode_data();
107 void mouseSelectionMode();
109 void horizontalAlignment_data();
110 void horizontalAlignment();
111 void horizontalAlignment_RightToLeft();
120 void passwordCharacter();
121 void cursorDelegate();
122 void cursorVisible();
123 void cursorRectangle();
125 void navigation_RTL();
127 void canPasteEmpty();
131 void openInputPanelOnClick();
132 void openInputPanelOnFocus();
133 void setHAlignClearCache();
134 void focusOutClearSelection();
137 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
138 void passwordEchoDelay();
140 void geometrySignals();
141 void testQtQuick11Attributes();
142 void testQtQuick11Attributes_data();
144 void preeditAutoScroll();
145 void preeditMicroFocus();
146 void inputContextMouseHandler();
147 void inputMethodComposing();
148 void cursorRectangleSize();
151 void simulateKey(QSGView *, int key);
153 QDeclarativeEngine engine;
154 QStringList standard;
155 QStringList colorStrings;
157 void tst_qsgtextinput::initTestCase()
161 void tst_qsgtextinput::cleanupTestCase()
165 tst_qsgtextinput::tst_qsgtextinput()
167 standard << "the quick brown fox jumped over the lazy dog"
168 << "It's supercalifragisiticexpialidocious!"
173 colorStrings << "aliceblue"
187 void tst_qsgtextinput::text()
190 QDeclarativeComponent textinputComponent(&engine);
191 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
192 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
194 QVERIFY(textinputObject != 0);
195 QCOMPARE(textinputObject->text(), QString(""));
197 delete textinputObject;
200 for (int i = 0; i < standard.size(); i++)
202 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
203 QDeclarativeComponent textinputComponent(&engine);
204 textinputComponent.setData(componentStr.toLatin1(), QUrl());
205 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
207 QVERIFY(textinputObject != 0);
208 QCOMPARE(textinputObject->text(), standard.at(i));
210 delete textinputObject;
215 void tst_qsgtextinput::width()
217 // uses Font metrics to find the width for standard
219 QDeclarativeComponent textinputComponent(&engine);
220 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
221 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
223 QVERIFY(textinputObject != 0);
224 QCOMPARE(textinputObject->width(), 0.0);
226 delete textinputObject;
229 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
231 for (int i = 0; i < standard.size(); i++)
234 qreal metricWidth = 0.0;
235 if (requiresUnhintedMetrics) {
236 QString s = standard.at(i);
237 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
239 QTextLayout layout(s);
240 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
243 option.setUseDesignMetrics(true);
244 layout.setTextOption(option);
247 layout.beginLayout();
249 QTextLine line = layout.createLine();
256 metricWidth = ceil(layout.boundingRect().width());
259 metricWidth = fm.width(standard.at(i));
262 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
263 QDeclarativeComponent textinputComponent(&engine);
264 textinputComponent.setData(componentStr.toLatin1(), QUrl());
265 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
267 QVERIFY(textinputObject != 0);
268 int delta = abs(int(int(textinputObject->width()) - metricWidth));
269 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
271 delete textinputObject;
275 void tst_qsgtextinput::font()
277 //test size, then bold, then italic, then family
279 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
280 QDeclarativeComponent textinputComponent(&engine);
281 textinputComponent.setData(componentStr.toLatin1(), QUrl());
282 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
284 QVERIFY(textinputObject != 0);
285 QCOMPARE(textinputObject->font().pointSize(), 40);
286 QCOMPARE(textinputObject->font().bold(), false);
287 QCOMPARE(textinputObject->font().italic(), false);
289 delete textinputObject;
293 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
294 QDeclarativeComponent textinputComponent(&engine);
295 textinputComponent.setData(componentStr.toLatin1(), QUrl());
296 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
298 QVERIFY(textinputObject != 0);
299 QCOMPARE(textinputObject->font().bold(), true);
300 QCOMPARE(textinputObject->font().italic(), false);
302 delete textinputObject;
306 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
307 QDeclarativeComponent textinputComponent(&engine);
308 textinputComponent.setData(componentStr.toLatin1(), QUrl());
309 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
311 QVERIFY(textinputObject != 0);
312 QCOMPARE(textinputObject->font().italic(), true);
313 QCOMPARE(textinputObject->font().bold(), false);
315 delete textinputObject;
319 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
320 QDeclarativeComponent textinputComponent(&engine);
321 textinputComponent.setData(componentStr.toLatin1(), QUrl());
322 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
324 QVERIFY(textinputObject != 0);
325 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
326 QCOMPARE(textinputObject->font().bold(), false);
327 QCOMPARE(textinputObject->font().italic(), false);
329 delete textinputObject;
333 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
334 QDeclarativeComponent textinputComponent(&engine);
335 textinputComponent.setData(componentStr.toLatin1(), QUrl());
336 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
338 QVERIFY(textinputObject != 0);
339 QCOMPARE(textinputObject->font().family(), QString(""));
341 delete textinputObject;
345 void tst_qsgtextinput::color()
348 for (int i = 0; i < colorStrings.size(); i++)
350 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
351 QDeclarativeComponent textinputComponent(&engine);
352 textinputComponent.setData(componentStr.toLatin1(), QUrl());
353 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
354 QVERIFY(textinputObject != 0);
355 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
357 delete textinputObject;
360 //test selection color
361 for (int i = 0; i < colorStrings.size(); i++)
363 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
364 QDeclarativeComponent textinputComponent(&engine);
365 textinputComponent.setData(componentStr.toLatin1(), QUrl());
366 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
367 QVERIFY(textinputObject != 0);
368 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
370 delete textinputObject;
373 //test selected text color
374 for (int i = 0; i < colorStrings.size(); i++)
376 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
377 QDeclarativeComponent textinputComponent(&engine);
378 textinputComponent.setData(componentStr.toLatin1(), QUrl());
379 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
380 QVERIFY(textinputObject != 0);
381 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
383 delete textinputObject;
387 QString colorStr = "#AA001234";
388 QColor testColor("#001234");
389 testColor.setAlpha(170);
391 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
392 QDeclarativeComponent textinputComponent(&engine);
393 textinputComponent.setData(componentStr.toLatin1(), QUrl());
394 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
396 QVERIFY(textinputObject != 0);
397 QCOMPARE(textinputObject->color(), testColor);
399 delete textinputObject;
403 void tst_qsgtextinput::selection()
405 QString testStr = standard[0];
406 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
407 QDeclarativeComponent textinputComponent(&engine);
408 textinputComponent.setData(componentStr.toLatin1(), QUrl());
409 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
410 QVERIFY(textinputObject != 0);
413 //Test selection follows cursor
414 for(int i=0; i<= testStr.size(); i++) {
415 textinputObject->setCursorPosition(i);
416 QCOMPARE(textinputObject->cursorPosition(), i);
417 QCOMPARE(textinputObject->selectionStart(), i);
418 QCOMPARE(textinputObject->selectionEnd(), i);
419 QVERIFY(textinputObject->selectedText().isNull());
422 textinputObject->setCursorPosition(0);
423 QVERIFY(textinputObject->cursorPosition() == 0);
424 QVERIFY(textinputObject->selectionStart() == 0);
425 QVERIFY(textinputObject->selectionEnd() == 0);
426 QVERIFY(textinputObject->selectedText().isNull());
428 // Verify invalid positions are ignored.
429 textinputObject->setCursorPosition(-1);
430 QVERIFY(textinputObject->cursorPosition() == 0);
431 QVERIFY(textinputObject->selectionStart() == 0);
432 QVERIFY(textinputObject->selectionEnd() == 0);
433 QVERIFY(textinputObject->selectedText().isNull());
435 textinputObject->setCursorPosition(textinputObject->text().count()+1);
436 QVERIFY(textinputObject->cursorPosition() == 0);
437 QVERIFY(textinputObject->selectionStart() == 0);
438 QVERIFY(textinputObject->selectionEnd() == 0);
439 QVERIFY(textinputObject->selectedText().isNull());
442 for(int i=0; i<= testStr.size(); i++) {
443 textinputObject->select(0,i);
444 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
446 for(int i=0; i<= testStr.size(); i++) {
447 textinputObject->select(i,testStr.size());
448 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
451 textinputObject->setCursorPosition(0);
452 QVERIFY(textinputObject->cursorPosition() == 0);
453 QVERIFY(textinputObject->selectionStart() == 0);
454 QVERIFY(textinputObject->selectionEnd() == 0);
455 QVERIFY(textinputObject->selectedText().isNull());
457 //Test Error Ignoring behaviour
458 textinputObject->setCursorPosition(0);
459 QVERIFY(textinputObject->selectedText().isNull());
460 textinputObject->select(-10,0);
461 QVERIFY(textinputObject->selectedText().isNull());
462 textinputObject->select(100,110);
463 QVERIFY(textinputObject->selectedText().isNull());
464 textinputObject->select(0,-10);
465 QVERIFY(textinputObject->selectedText().isNull());
466 textinputObject->select(0,100);
467 QVERIFY(textinputObject->selectedText().isNull());
468 textinputObject->select(0,10);
469 QVERIFY(textinputObject->selectedText().size() == 10);
470 textinputObject->select(-10,10);
471 QVERIFY(textinputObject->selectedText().size() == 10);
472 textinputObject->select(100,101);
473 QVERIFY(textinputObject->selectedText().size() == 10);
474 textinputObject->select(0,-10);
475 QVERIFY(textinputObject->selectedText().size() == 10);
476 textinputObject->select(0,100);
477 QVERIFY(textinputObject->selectedText().size() == 10);
479 textinputObject->deselect();
480 QVERIFY(textinputObject->selectedText().isNull());
481 textinputObject->select(0,10);
482 QVERIFY(textinputObject->selectedText().size() == 10);
483 textinputObject->deselect();
484 QVERIFY(textinputObject->selectedText().isNull());
486 delete textinputObject;
489 void tst_qsgtextinput::isRightToLeft_data()
491 QTest::addColumn<QString>("text");
492 QTest::addColumn<bool>("emptyString");
493 QTest::addColumn<bool>("firstCharacter");
494 QTest::addColumn<bool>("lastCharacter");
495 QTest::addColumn<bool>("middleCharacter");
496 QTest::addColumn<bool>("startString");
497 QTest::addColumn<bool>("midString");
498 QTest::addColumn<bool>("endString");
500 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
501 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
502 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
503 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
504 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
505 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;
506 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
509 void tst_qsgtextinput::isRightToLeft()
511 QFETCH(QString, text);
512 QFETCH(bool, emptyString);
513 QFETCH(bool, firstCharacter);
514 QFETCH(bool, lastCharacter);
515 QFETCH(bool, middleCharacter);
516 QFETCH(bool, startString);
517 QFETCH(bool, midString);
518 QFETCH(bool, endString);
520 QSGTextInput textInput;
521 textInput.setText(text);
523 // first test that the right string is delivered to the QString::isRightToLeft()
524 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
525 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
526 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
527 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
528 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
529 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
531 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
532 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
534 // then test that the feature actually works
535 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
536 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
537 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
538 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
539 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
540 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
542 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
543 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
546 void tst_qsgtextinput::moveCursorSelection_data()
548 QTest::addColumn<QString>("testStr");
549 QTest::addColumn<int>("cursorPosition");
550 QTest::addColumn<int>("movePosition");
551 QTest::addColumn<QSGTextInput::SelectionMode>("mode");
552 QTest::addColumn<int>("selectionStart");
553 QTest::addColumn<int>("selectionEnd");
554 QTest::addColumn<bool>("reversible");
556 // () contains the text selected by the cursor.
557 // <> contains the actual selection.
559 QTest::newRow("(t)he|characters")
560 << standard[0] << 0 << 1 << QSGTextInput::SelectCharacters << 0 << 1 << true;
561 QTest::newRow("do(g)|characters")
562 << standard[0] << 43 << 44 << QSGTextInput::SelectCharacters << 43 << 44 << true;
563 QTest::newRow("jum(p)ed|characters")
564 << standard[0] << 23 << 24 << QSGTextInput::SelectCharacters << 23 << 24 << true;
565 QTest::newRow("jumped( )over|characters")
566 << standard[0] << 26 << 27 << QSGTextInput::SelectCharacters << 26 << 27 << true;
567 QTest::newRow("(the )|characters")
568 << standard[0] << 0 << 4 << QSGTextInput::SelectCharacters << 0 << 4 << true;
569 QTest::newRow("( dog)|characters")
570 << standard[0] << 40 << 44 << QSGTextInput::SelectCharacters << 40 << 44 << true;
571 QTest::newRow("( jumped )|characters")
572 << standard[0] << 19 << 27 << QSGTextInput::SelectCharacters << 19 << 27 << true;
573 QTest::newRow("th(e qu)ick|characters")
574 << standard[0] << 2 << 6 << QSGTextInput::SelectCharacters << 2 << 6 << true;
575 QTest::newRow("la(zy d)og|characters")
576 << standard[0] << 38 << 42 << QSGTextInput::SelectCharacters << 38 << 42 << true;
577 QTest::newRow("jum(ped ov)er|characters")
578 << standard[0] << 23 << 29 << QSGTextInput::SelectCharacters << 23 << 29 << true;
579 QTest::newRow("()the|characters")
580 << standard[0] << 0 << 0 << QSGTextInput::SelectCharacters << 0 << 0 << true;
581 QTest::newRow("dog()|characters")
582 << standard[0] << 44 << 44 << QSGTextInput::SelectCharacters << 44 << 44 << true;
583 QTest::newRow("jum()ped|characters")
584 << standard[0] << 23 << 23 << QSGTextInput::SelectCharacters << 23 << 23 << true;
586 QTest::newRow("<(t)he>|words")
587 << standard[0] << 0 << 1 << QSGTextInput::SelectWords << 0 << 3 << true;
588 QTest::newRow("<do(g)>|words")
589 << standard[0] << 43 << 44 << QSGTextInput::SelectWords << 41 << 44 << true;
590 QTest::newRow("<jum(p)ed>|words")
591 << standard[0] << 23 << 24 << QSGTextInput::SelectWords << 20 << 26 << true;
592 QTest::newRow("<jumped( )>over|words,ltr")
593 << standard[0] << 26 << 27 << QSGTextInput::SelectWords << 20 << 27 << false;
594 QTest::newRow("jumped<( )over>|words,rtl")
595 << standard[0] << 27 << 26 << QSGTextInput::SelectWords << 26 << 31 << false;
596 QTest::newRow("<(the )>quick|words,ltr")
597 << standard[0] << 0 << 4 << QSGTextInput::SelectWords << 0 << 4 << false;
598 QTest::newRow("<(the )quick>|words,rtl")
599 << standard[0] << 4 << 0 << QSGTextInput::SelectWords << 0 << 9 << false;
600 QTest::newRow("<lazy( dog)>|words,ltr")
601 << standard[0] << 40 << 44 << QSGTextInput::SelectWords << 36 << 44 << false;
602 QTest::newRow("lazy<( dog)>|words,rtl")
603 << standard[0] << 44 << 40 << QSGTextInput::SelectWords << 40 << 44 << false;
604 QTest::newRow("<fox( jumped )>over|words,ltr")
605 << standard[0] << 19 << 27 << QSGTextInput::SelectWords << 16 << 27 << false;
606 QTest::newRow("fox<( jumped )over>|words,rtl")
607 << standard[0] << 27 << 19 << QSGTextInput::SelectWords << 19 << 31 << false;
608 QTest::newRow("<th(e qu)ick>|words")
609 << standard[0] << 2 << 6 << QSGTextInput::SelectWords << 0 << 9 << true;
610 QTest::newRow("<la(zy d)og|words>")
611 << standard[0] << 38 << 42 << QSGTextInput::SelectWords << 36 << 44 << true;
612 QTest::newRow("<jum(ped ov)er>|words")
613 << standard[0] << 23 << 29 << QSGTextInput::SelectWords << 20 << 31 << true;
614 QTest::newRow("<()>the|words")
615 << standard[0] << 0 << 0 << QSGTextInput::SelectWords << 0 << 0 << true;
616 QTest::newRow("dog<()>|words")
617 << standard[0] << 44 << 44 << QSGTextInput::SelectWords << 44 << 44 << true;
618 QTest::newRow("jum<()>ped|words")
619 << standard[0] << 23 << 23 << QSGTextInput::SelectWords << 23 << 23 << true;
621 QTest::newRow("Hello<(,)> |words")
622 << standard[2] << 5 << 6 << QSGTextInput::SelectWords << 5 << 6 << true;
623 QTest::newRow("Hello<(, )>world|words,ltr")
624 << standard[2] << 5 << 7 << QSGTextInput::SelectWords << 5 << 7 << false;
625 QTest::newRow("Hello<(, )world>|words,rtl")
626 << standard[2] << 7 << 5 << QSGTextInput::SelectWords << 5 << 12 << false;
627 QTest::newRow("<Hel(lo, )>world|words,ltr")
628 << standard[2] << 3 << 7 << QSGTextInput::SelectWords << 0 << 7 << false;
629 QTest::newRow("<Hel(lo, )world>|words,rtl")
630 << standard[2] << 7 << 3 << QSGTextInput::SelectWords << 0 << 12 << false;
631 QTest::newRow("<Hel(lo)>,|words")
632 << standard[2] << 3 << 5 << QSGTextInput::SelectWords << 0 << 5 << true;
633 QTest::newRow("Hello<()>,|words")
634 << standard[2] << 5 << 5 << QSGTextInput::SelectWords << 5 << 5 << true;
635 QTest::newRow("Hello,<()>|words")
636 << standard[2] << 6 << 6 << QSGTextInput::SelectWords << 6 << 6 << true;
637 QTest::newRow("Hello<,( )>world|words,ltr")
638 << standard[2] << 6 << 7 << QSGTextInput::SelectWords << 5 << 7 << false;
639 QTest::newRow("Hello,<( )world>|words,rtl")
640 << standard[2] << 7 << 6 << QSGTextInput::SelectWords << 6 << 12 << false;
641 QTest::newRow("Hello<,( world)>|words,ltr")
642 << standard[2] << 6 << 12 << QSGTextInput::SelectWords << 5 << 12 << false;
643 QTest::newRow("Hello,<( world)>|words,rtl")
644 << standard[2] << 12 << 6 << QSGTextInput::SelectWords << 6 << 12 << false;
645 QTest::newRow("Hello<,( world!)>|words,ltr")
646 << standard[2] << 6 << 13 << QSGTextInput::SelectWords << 5 << 13 << false;
647 QTest::newRow("Hello,<( world!)>|words,rtl")
648 << standard[2] << 13 << 6 << QSGTextInput::SelectWords << 6 << 13 << false;
649 QTest::newRow("Hello<(, world!)>|words")
650 << standard[2] << 5 << 13 << QSGTextInput::SelectWords << 5 << 13 << true;
651 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
653 // QTest::newRow("world<(!)>|words")
654 // << standard[2] << 12 << 13 << QSGTextInput::SelectWords << 12 << 13 << true;
655 QTest::newRow("world!<()>)|words")
656 << standard[2] << 13 << 13 << QSGTextInput::SelectWords << 13 << 13 << true;
657 QTest::newRow("world<()>!)|words")
658 << standard[2] << 12 << 12 << QSGTextInput::SelectWords << 12 << 12 << true;
660 QTest::newRow("<(,)>olleH |words")
661 << standard[3] << 7 << 8 << QSGTextInput::SelectWords << 7 << 8 << true;
662 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
663 << standard[3] << 6 << 8 << QSGTextInput::SelectWords << 1 << 8 << false;
664 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
665 << standard[3] << 8 << 6 << QSGTextInput::SelectWords << 6 << 8 << false;
666 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
667 << standard[3] << 6 << 10 << QSGTextInput::SelectWords << 1 << 13 << false;
668 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
669 << standard[3] << 10 << 6 << QSGTextInput::SelectWords << 6 << 13 << false;
670 QTest::newRow(",<(ol)leH>,|words")
671 << standard[3] << 8 << 10 << QSGTextInput::SelectWords << 8 << 13 << true;
672 QTest::newRow(",<()>olleH|words")
673 << standard[3] << 8 << 8 << QSGTextInput::SelectWords << 8 << 8 << true;
674 QTest::newRow("<()>,olleH|words")
675 << standard[3] << 7 << 7 << QSGTextInput::SelectWords << 7 << 7 << true;
676 QTest::newRow("<dlrow( )>,olleH|words,ltr")
677 << standard[3] << 6 << 7 << QSGTextInput::SelectWords << 1 << 7 << false;
678 QTest::newRow("dlrow<( ),>olleH|words,rtl")
679 << standard[3] << 7 << 6 << QSGTextInput::SelectWords << 6 << 8 << false;
680 QTest::newRow("<(dlrow )>,olleH|words,ltr")
681 << standard[3] << 1 << 7 << QSGTextInput::SelectWords << 1 << 7 << false;
682 QTest::newRow("<(dlrow ),>olleH|words,rtl")
683 << standard[3] << 7 << 1 << QSGTextInput::SelectWords << 1 << 8 << false;
684 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
685 << standard[3] << 0 << 7 << QSGTextInput::SelectWords << 0 << 7 << false;
686 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
687 << standard[3] << 7 << 0 << QSGTextInput::SelectWords << 0 << 8 << false;
688 QTest::newRow("(!dlrow ,)olleH|words")
689 << standard[3] << 0 << 8 << QSGTextInput::SelectWords << 0 << 8 << true;
690 QTest::newRow("<(!)>dlrow|words")
691 << standard[3] << 0 << 1 << QSGTextInput::SelectWords << 0 << 1 << true;
692 QTest::newRow("<()>!dlrow|words")
693 << standard[3] << 0 << 0 << QSGTextInput::SelectWords << 0 << 0 << true;
694 QTest::newRow("!<()>dlrow|words")
695 << standard[3] << 1 << 1 << QSGTextInput::SelectWords << 1 << 1 << true;
697 QTest::newRow(" <s(pac)ey> text |words")
698 << standard[4] << 1 << 4 << QSGTextInput::SelectWords << 1 << 7 << true;
699 QTest::newRow(" spacey <t(ex)t> |words")
700 << standard[4] << 11 << 13 << QSGTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
701 QTest::newRow("<( )>spacey text |words|ltr")
702 << standard[4] << 0 << 1 << QSGTextInput::SelectWords << 0 << 1 << false;
703 QTest::newRow("<( )spacey> text |words|rtl")
704 << standard[4] << 1 << 0 << QSGTextInput::SelectWords << 0 << 7 << false;
705 QTest::newRow("spacey <text( )>|words|ltr")
706 << standard[4] << 14 << 15 << QSGTextInput::SelectWords << 10 << 15 << false;
708 // QTest::newRow("spacey text<( )>|words|rtl")
709 // << standard[4] << 15 << 14 << QSGTextInput::SelectWords << 14 << 15 << false;
710 QTest::newRow("<()> spacey text |words")
711 << standard[4] << 0 << 0 << QSGTextInput::SelectWords << 0 << 0 << false;
712 QTest::newRow(" spacey text <()>|words")
713 << standard[4] << 15 << 15 << QSGTextInput::SelectWords << 15 << 15 << false;
716 void tst_qsgtextinput::moveCursorSelection()
718 QFETCH(QString, testStr);
719 QFETCH(int, cursorPosition);
720 QFETCH(int, movePosition);
721 QFETCH(QSGTextInput::SelectionMode, mode);
722 QFETCH(int, selectionStart);
723 QFETCH(int, selectionEnd);
724 QFETCH(bool, reversible);
726 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
727 QDeclarativeComponent textinputComponent(&engine);
728 textinputComponent.setData(componentStr.toLatin1(), QUrl());
729 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
730 QVERIFY(textinputObject != 0);
732 textinputObject->setCursorPosition(cursorPosition);
733 textinputObject->moveCursorSelection(movePosition, mode);
735 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
736 QCOMPARE(textinputObject->selectionStart(), selectionStart);
737 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
740 textinputObject->setCursorPosition(movePosition);
741 textinputObject->moveCursorSelection(cursorPosition, mode);
743 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
744 QCOMPARE(textinputObject->selectionStart(), selectionStart);
745 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
748 delete textinputObject;
751 void tst_qsgtextinput::moveCursorSelectionSequence_data()
753 QTest::addColumn<QString>("testStr");
754 QTest::addColumn<int>("cursorPosition");
755 QTest::addColumn<int>("movePosition1");
756 QTest::addColumn<int>("movePosition2");
757 QTest::addColumn<int>("selection1Start");
758 QTest::addColumn<int>("selection1End");
759 QTest::addColumn<int>("selection2Start");
760 QTest::addColumn<int>("selection2End");
762 // () contains the text selected by the cursor.
763 // <> contains the actual selection.
764 // ^ is the revised cursor position.
765 // {} contains the revised selection.
767 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
772 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
777 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
782 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
787 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
792 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
797 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
802 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
807 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
812 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
817 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
822 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
827 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
832 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
837 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
842 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
847 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
852 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
858 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
863 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
869 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
874 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
880 QTest::newRow("{<(^} sp)acey> text |ltr")
885 QTest::newRow("{<( ^}sp)acey> text |ltr")
890 QTest::newRow("<( {s^p)acey>} text |rtl")
895 QTest::newRow("<( {^sp)acey>} text |rtl")
901 QTest::newRow(" spacey <te(xt {^)>}|rtl")
907 // QTest::newRow(" spacey <te(xt{^ )>}|rtl")
912 QTest::newRow(" spacey {<te(x^t} )>|ltr")
918 // QTest::newRow(" spacey {<te(xt^} )>|ltr")
925 void tst_qsgtextinput::moveCursorSelectionSequence()
927 QFETCH(QString, testStr);
928 QFETCH(int, cursorPosition);
929 QFETCH(int, movePosition1);
930 QFETCH(int, movePosition2);
931 QFETCH(int, selection1Start);
932 QFETCH(int, selection1End);
933 QFETCH(int, selection2Start);
934 QFETCH(int, selection2End);
936 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
937 QDeclarativeComponent textinputComponent(&engine);
938 textinputComponent.setData(componentStr.toLatin1(), QUrl());
939 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
940 QVERIFY(textinputObject != 0);
942 textinputObject->setCursorPosition(cursorPosition);
944 textinputObject->moveCursorSelection(movePosition1, QSGTextInput::SelectWords);
945 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
946 QCOMPARE(textinputObject->selectionStart(), selection1Start);
947 QCOMPARE(textinputObject->selectionEnd(), selection1End);
949 textinputObject->moveCursorSelection(movePosition2, QSGTextInput::SelectWords);
950 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
951 QCOMPARE(textinputObject->selectionStart(), selection2Start);
952 QCOMPARE(textinputObject->selectionEnd(), selection2End);
954 delete textinputObject;
957 void tst_qsgtextinput::dragMouseSelection()
959 QString qmlfile = SRCDIR "/data/mouseselection_true.qml";
961 QSGView canvas(QUrl::fromLocalFile(qmlfile));
964 canvas.requestActivateWindow();
965 QTest::qWaitForWindowShown(&canvas);
967 QTRY_COMPARE(canvas.windowState(), Qt::WindowActive);
969 QVERIFY(canvas.rootObject() != 0);
970 QSGTextInput *textInputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
971 QVERIFY(textInputObject != 0);
973 // press-and-drag-and-release from x1 to x2
976 int y = textInputObject->height()/2;
977 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
979 QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
980 QApplication::sendEvent(&canvas, &mv);
982 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
984 QString str1 = textInputObject->selectedText();
985 QVERIFY(str1.length() > 3);
987 // press and drag the current selection.
990 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
992 QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
993 QApplication::sendEvent(&canvas, &mv);
995 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
996 QString str2 = textInputObject->selectedText();
997 QVERIFY(str2.length() > 3);
999 QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and doesn't not the first moved.
1002 void tst_qsgtextinput::mouseSelectionMode_data()
1004 QTest::addColumn<QString>("qmlfile");
1005 QTest::addColumn<bool>("selectWords");
1008 QTest::newRow("SelectWords") << SRCDIR "/data/mouseselectionmode_words.qml" << true;
1009 QTest::newRow("SelectCharacters") << SRCDIR "/data/mouseselectionmode_characters.qml" << false;
1010 QTest::newRow("default") << SRCDIR "/data/mouseselectionmode_default.qml" << false;
1013 void tst_qsgtextinput::mouseSelectionMode()
1015 QFETCH(QString, qmlfile);
1016 QFETCH(bool, selectWords);
1018 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1020 QSGView canvas(QUrl::fromLocalFile(qmlfile));
1023 canvas.requestActivateWindow();
1024 QTest::qWaitForWindowShown(&canvas);
1025 QTRY_COMPARE(canvas.windowState(), Qt::WindowActive);
1027 QVERIFY(canvas.rootObject() != 0);
1028 QSGTextInput *textInputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
1029 QVERIFY(textInputObject != 0);
1031 // press-and-drag-and-release from x1 to x2
1034 int y = textInputObject->height()/2;
1035 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1036 //QTest::mouseMove(&canvas, canvas.mapFromScene(QPoint(x2,y))); // doesn't work
1037 QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1038 QApplication::sendEvent(&canvas, &mv);
1039 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1040 QString str = textInputObject->selectedText();
1042 QCOMPARE(str, text);
1044 QVERIFY(str.length() > 3);
1045 QVERIFY(str != text);
1049 void tst_qsgtextinput::horizontalAlignment_data()
1051 QTest::addColumn<int>("hAlign");
1052 QTest::addColumn<QString>("expectfile");
1054 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1055 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1056 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1059 void tst_qsgtextinput::horizontalAlignment()
1061 QFETCH(int, hAlign);
1062 QFETCH(QString, expectfile);
1064 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/horizontalAlignment.qml"));
1067 canvas.requestActivateWindow();
1068 QTest::qWaitForWindowShown(&canvas);
1069 QTRY_COMPARE(canvas.windowState(), Qt::WindowActive);
1070 QObject *ob = canvas.rootObject();
1072 ob->setProperty("horizontalAlignment",hAlign);
1073 QImage actual = canvas.grabFrameBuffer();
1075 expectfile = createExpectedFileIfNotFound(expectfile, actual);
1077 QImage expect(expectfile);
1079 QCOMPARE(actual,expect);
1082 void tst_qsgtextinput::horizontalAlignment_RightToLeft()
1084 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/horizontalAlignment_RightToLeft.qml"));
1085 QSGTextInput *textInput = canvas.rootObject()->findChild<QSGTextInput*>("text");
1086 QVERIFY(textInput != 0);
1089 const QString rtlText = textInput->text();
1091 QSGTextInputPrivate *textInputPrivate = QSGTextInputPrivate::get(textInput);
1092 QVERIFY(textInputPrivate != 0);
1093 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1095 // implicit alignment should follow the reading direction of RTL text
1096 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1097 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1098 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1100 // explicitly left aligned
1101 textInput->setHAlign(QSGTextInput::AlignLeft);
1102 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
1103 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1104 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1106 // explicitly right aligned
1107 textInput->setHAlign(QSGTextInput::AlignRight);
1108 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1109 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1110 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1112 // explicitly center aligned
1113 textInput->setHAlign(QSGTextInput::AlignHCenter);
1114 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1115 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignHCenter);
1116 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1117 QVERIFY(-textInputPrivate->hscroll + textInputPrivate->width > canvas.width()/2);
1119 // reseted alignment should go back to following the text reading direction
1120 textInput->resetHAlign();
1121 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1122 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1123 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1125 // mirror the text item
1126 QSGItemPrivate::get(textInput)->setLayoutMirror(true);
1128 // mirrored implicit alignment should continue to follow the reading direction of the text
1129 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1130 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1131 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1133 // explicitly right aligned behaves as left aligned
1134 textInput->setHAlign(QSGTextInput::AlignRight);
1135 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1136 QCOMPARE(textInput->effectiveHAlign(), QSGTextInput::AlignLeft);
1137 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1139 // mirrored explicitly left aligned behaves as right aligned
1140 textInput->setHAlign(QSGTextInput::AlignLeft);
1141 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
1142 QCOMPARE(textInput->effectiveHAlign(), QSGTextInput::AlignRight);
1143 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1145 // disable mirroring
1146 QSGItemPrivate::get(textInput)->setLayoutMirror(false);
1147 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1148 textInput->resetHAlign();
1150 // English text should be implicitly left aligned
1151 textInput->setText("Hello world!");
1152 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
1153 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1155 QApplication::setActiveWindow(&canvas);
1156 QTest::qWaitForWindowShown(&canvas);
1157 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
1159 // If there is no commited text, the preedit text should determine the alignment.
1160 textInput->setText(QString());
1161 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(&canvas, &ev); }
1162 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1163 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(&canvas, &ev); }
1164 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
1166 #ifndef Q_OS_MAC // QTBUG-18040
1167 // empty text with implicit alignment follows the system locale-based
1168 // keyboard input direction from QApplication::keyboardInputDirection
1169 textInput->setText("");
1170 QCOMPARE(textInput->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
1171 QSGTextInput::AlignLeft : QSGTextInput::AlignRight);
1172 if (QApplication::keyboardInputDirection() == Qt::LeftToRight)
1173 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1175 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1176 textInput->setHAlign(QSGTextInput::AlignRight);
1177 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1178 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1181 #ifndef Q_OS_MAC // QTBUG-18040
1182 // alignment of TextInput with no text set to it
1183 QString componentStr = "import QtQuick 2.0\nTextInput {}";
1184 QDeclarativeComponent textComponent(&engine);
1185 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1186 QSGTextInput *textObject = qobject_cast<QSGTextInput*>(textComponent.create());
1187 QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
1188 QSGTextInput::AlignLeft : QSGTextInput::AlignRight);
1193 void tst_qsgtextinput::positionAt()
1195 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/positionAt.qml"));
1196 QVERIFY(canvas.rootObject() != 0);
1198 canvas.requestActivateWindow();
1199 QTest::qWaitForWindowShown(&canvas);
1201 QSGTextInput *textinputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
1202 QVERIFY(textinputObject != 0);
1204 // Check autoscrolled...
1205 QFontMetrics fm(textinputObject->font());
1207 int pos = textinputObject->positionAt(textinputObject->width()/2);
1209 int textLeftWidth = 0;
1210 if (!qmlDisableDistanceField()) {
1212 QTextLayout layout(textinputObject->text().left(pos));
1216 option.setUseDesignMetrics(true);
1217 layout.setTextOption(option);
1220 layout.beginLayout();
1221 QTextLine line = layout.createLine();
1224 textLeftWidth = ceil(line.horizontalAdvance());
1227 QTextLayout layout(textinputObject->text());
1231 option.setUseDesignMetrics(true);
1232 layout.setTextOption(option);
1235 layout.beginLayout();
1236 QTextLine line = layout.createLine();
1239 textWidth = ceil(line.horizontalAdvance());
1242 textWidth = fm.width(textinputObject->text());
1243 textLeftWidth = fm.width(textinputObject->text().left(pos));
1246 int diff = abs(textWidth - (textLeftWidth+textinputObject->width()/2));
1249 QEXPECT_FAIL("", "QTBUG-21011 fails", Continue);
1252 // some tollerance for different fonts.
1259 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1260 QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorBetweenCharacters), pos + 1);
1261 QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorOnCharacter), pos);
1263 // Check without autoscroll...
1264 textinputObject->setAutoScroll(false);
1265 pos = textinputObject->positionAt(textinputObject->width()/2);
1267 if (!qmlDisableDistanceField()) {
1269 QTextLayout layout(textinputObject->text().left(pos));
1273 option.setUseDesignMetrics(true);
1274 layout.setTextOption(option);
1277 layout.beginLayout();
1278 QTextLine line = layout.createLine();
1281 textLeftWidth = ceil(line.horizontalAdvance());
1284 textLeftWidth = fm.width(textinputObject->text().left(pos));
1287 diff = abs(int(textLeftWidth-textinputObject->width()/2));
1290 QEXPECT_FAIL("", "QTBUG-21011 fails", Continue);
1293 // some tollerance for different fonts.
1300 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1301 QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorBetweenCharacters), pos + 1);
1302 QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorOnCharacter), pos);
1304 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1305 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1307 QString preeditText = textinputObject->text().mid(0, pos);
1308 textinputObject->setText(textinputObject->text().mid(pos));
1309 textinputObject->setCursorPosition(0);
1311 QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1312 QApplication::sendEvent(&canvas, &inputEvent);
1314 // Check all points within the preedit text return the same position.
1315 QCOMPARE(textinputObject->positionAt(0), 0);
1316 QCOMPARE(textinputObject->positionAt(x0 / 2), 0);
1317 QCOMPARE(textinputObject->positionAt(x0), 0);
1319 // Verify positioning returns to normal after the preedit text.
1320 QCOMPARE(textinputObject->positionAt(x1), 1);
1321 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1324 void tst_qsgtextinput::maxLength()
1326 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/maxLength.qml"));
1327 QVERIFY(canvas.rootObject() != 0);
1329 canvas.requestActivateWindow();
1330 QTest::qWaitForWindowShown(&canvas);
1332 QSGTextInput *textinputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
1333 QVERIFY(textinputObject != 0);
1334 QVERIFY(textinputObject->text().isEmpty());
1335 QVERIFY(textinputObject->maxLength() == 10);
1336 foreach(const QString &str, standard){
1337 QVERIFY(textinputObject->text().length() <= 10);
1338 textinputObject->setText(str);
1339 QVERIFY(textinputObject->text().length() <= 10);
1342 textinputObject->setText("");
1343 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1344 for(int i=0; i<20; i++){
1345 QCOMPARE(textinputObject->text().length(), qMin(i,10));
1346 //simulateKey(&canvas, Qt::Key_A);
1347 QTest::keyPress(&canvas, Qt::Key_A);
1348 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1352 void tst_qsgtextinput::masks()
1354 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1355 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1356 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/masks.qml"));
1358 canvas.requestActivateWindow();
1359 QVERIFY(canvas.rootObject() != 0);
1360 QSGTextInput *textinputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
1361 QVERIFY(textinputObject != 0);
1362 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1363 QVERIFY(textinputObject->text().length() == 0);
1364 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1365 for(int i=0; i<10; i++){
1366 QCOMPARE(qMin(i,8), textinputObject->text().length());
1367 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1368 //simulateKey(&canvas, Qt::Key_A);
1369 QTest::keyPress(&canvas, Qt::Key_A);
1370 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1374 void tst_qsgtextinput::validators()
1376 // Note that this test assumes that the validators are working properly
1377 // so you may need to run their tests first. All validators are checked
1378 // here to ensure that their exposure to QML is working.
1380 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/validators.qml"));
1382 canvas.requestActivateWindow();
1384 QVERIFY(canvas.rootObject() != 0);
1386 QSGTextInput *intInput = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1388 intInput->setFocus(true);
1389 QTRY_VERIFY(intInput->hasActiveFocus());
1390 QTest::keyPress(&canvas, Qt::Key_1);
1391 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1392 QCOMPARE(intInput->text(), QLatin1String("1"));
1393 QCOMPARE(intInput->hasAcceptableInput(), false);
1394 QTest::keyPress(&canvas, Qt::Key_2);
1395 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1396 QCOMPARE(intInput->text(), QLatin1String("1"));
1397 QCOMPARE(intInput->hasAcceptableInput(), false);
1398 QTest::keyPress(&canvas, Qt::Key_1);
1399 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1400 QCOMPARE(intInput->text(), QLatin1String("11"));
1401 QCOMPARE(intInput->hasAcceptableInput(), true);
1402 QTest::keyPress(&canvas, Qt::Key_0);
1403 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1404 QCOMPARE(intInput->text(), QLatin1String("11"));
1405 QCOMPARE(intInput->hasAcceptableInput(), true);
1407 QSGTextInput *dblInput = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1408 QTRY_VERIFY(dblInput);
1409 dblInput->setFocus(true);
1410 QVERIFY(dblInput->hasActiveFocus() == true);
1411 QTest::keyPress(&canvas, Qt::Key_1);
1412 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1413 QCOMPARE(dblInput->text(), QLatin1String("1"));
1414 QCOMPARE(dblInput->hasAcceptableInput(), false);
1415 QTest::keyPress(&canvas, Qt::Key_2);
1416 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1417 QCOMPARE(dblInput->text(), QLatin1String("12"));
1418 QCOMPARE(dblInput->hasAcceptableInput(), true);
1419 QTest::keyPress(&canvas, Qt::Key_Period);
1420 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1421 QCOMPARE(dblInput->text(), QLatin1String("12."));
1422 QCOMPARE(dblInput->hasAcceptableInput(), true);
1423 QTest::keyPress(&canvas, Qt::Key_1);
1424 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1425 QCOMPARE(dblInput->text(), QLatin1String("12.1"));
1426 QCOMPARE(dblInput->hasAcceptableInput(), true);
1427 QTest::keyPress(&canvas, Qt::Key_1);
1428 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1429 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1430 QCOMPARE(dblInput->hasAcceptableInput(), true);
1431 QTest::keyPress(&canvas, Qt::Key_1);
1432 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1433 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1434 QCOMPARE(dblInput->hasAcceptableInput(), true);
1436 QSGTextInput *strInput = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1437 QTRY_VERIFY(strInput);
1438 strInput->setFocus(true);
1439 QVERIFY(strInput->hasActiveFocus() == true);
1440 QTest::keyPress(&canvas, Qt::Key_1);
1441 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1442 QCOMPARE(strInput->text(), QLatin1String(""));
1443 QCOMPARE(strInput->hasAcceptableInput(), false);
1444 QTest::keyPress(&canvas, Qt::Key_A);
1445 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1446 QCOMPARE(strInput->text(), QLatin1String("a"));
1447 QCOMPARE(strInput->hasAcceptableInput(), false);
1448 QTest::keyPress(&canvas, Qt::Key_A);
1449 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1450 QCOMPARE(strInput->text(), QLatin1String("aa"));
1451 QCOMPARE(strInput->hasAcceptableInput(), true);
1452 QTest::keyPress(&canvas, Qt::Key_A);
1453 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1454 QCOMPARE(strInput->text(), QLatin1String("aaa"));
1455 QCOMPARE(strInput->hasAcceptableInput(), true);
1456 QTest::keyPress(&canvas, Qt::Key_A);
1457 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1458 QCOMPARE(strInput->text(), QLatin1String("aaaa"));
1459 QCOMPARE(strInput->hasAcceptableInput(), true);
1460 QTest::keyPress(&canvas, Qt::Key_A);
1461 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1462 QCOMPARE(strInput->text(), QLatin1String("aaaa"));
1463 QCOMPARE(strInput->hasAcceptableInput(), true);
1466 void tst_qsgtextinput::inputMethods()
1468 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/inputmethods.qml"));
1470 canvas.requestActivateWindow();
1471 QTest::qWaitForWindowShown(&canvas);
1473 // test input method hints
1474 QVERIFY(canvas.rootObject() != 0);
1475 QSGTextInput *input = qobject_cast<QSGTextInput *>(canvas.rootObject());
1476 QVERIFY(input != 0);
1477 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1478 input->setInputMethodHints(Qt::ImhUppercaseOnly);
1479 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1481 input->setFocus(true);
1482 QVERIFY(input->hasActiveFocus() == true);
1483 // test that input method event is committed
1484 QInputMethodEvent event;
1485 event.setCommitString( "My ", -12, 0);
1486 QApplication::sendEvent(&canvas, &event);
1487 QCOMPARE(input->text(), QString("My Hello world!"));
1489 input->setCursorPosition(2);
1490 event.setCommitString("Your", -2, 2);
1491 QApplication::sendEvent(&canvas, &event);
1492 QCOMPARE(input->text(), QString("Your Hello world!"));
1493 QCOMPARE(input->cursorPosition(), 4);
1495 input->setCursorPosition(7);
1496 event.setCommitString("Goodbye", -2, 5);
1497 QApplication::sendEvent(&canvas, &event);
1498 QCOMPARE(input->text(), QString("Your Goodbye world!"));
1499 QCOMPARE(input->cursorPosition(), 12);
1501 input->setCursorPosition(8);
1502 event.setCommitString("Our", -8, 4);
1503 QApplication::sendEvent(&canvas, &event);
1504 QCOMPARE(input->text(), QString("Our Goodbye world!"));
1505 QCOMPARE(input->cursorPosition(), 7);
1509 TextInput element should only handle left/right keys until the cursor reaches
1510 the extent of the text, then they should ignore the keys.
1513 void tst_qsgtextinput::navigation()
1515 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/navigation.qml"));
1517 canvas.requestActivateWindow();
1519 QVERIFY(canvas.rootObject() != 0);
1521 QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1523 QVERIFY(input != 0);
1524 input->setCursorPosition(0);
1525 QTRY_VERIFY(input->hasActiveFocus() == true);
1526 simulateKey(&canvas, Qt::Key_Left);
1527 QVERIFY(input->hasActiveFocus() == false);
1528 simulateKey(&canvas, Qt::Key_Right);
1529 QVERIFY(input->hasActiveFocus() == true);
1530 //QT-2944: If text is selected, ensure we deselect upon cursor motion
1531 input->setCursorPosition(input->text().length());
1532 input->select(0,input->text().length());
1533 QVERIFY(input->selectionStart() != input->selectionEnd());
1534 simulateKey(&canvas, Qt::Key_Right);
1535 QVERIFY(input->selectionStart() == input->selectionEnd());
1536 QVERIFY(input->selectionStart() == input->text().length());
1537 QVERIFY(input->hasActiveFocus() == true);
1538 simulateKey(&canvas, Qt::Key_Right);
1539 QVERIFY(input->hasActiveFocus() == false);
1540 simulateKey(&canvas, Qt::Key_Left);
1541 QVERIFY(input->hasActiveFocus() == true);
1543 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1544 input->setCursorPosition(2);
1545 QCOMPARE(input->cursorPosition(),2);
1546 simulateKey(&canvas, Qt::Key_Up);
1547 QCOMPARE(input->cursorPosition(),2);
1548 simulateKey(&canvas, Qt::Key_Down);
1549 QCOMPARE(input->cursorPosition(),2);
1552 void tst_qsgtextinput::navigation_RTL()
1554 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/navigation.qml"));
1556 canvas.requestActivateWindow();
1558 QVERIFY(canvas.rootObject() != 0);
1560 QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1562 QVERIFY(input != 0);
1563 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1564 input->setText(QString::fromUtf16(arabic_str, 11));
1566 input->setCursorPosition(0);
1567 QTRY_VERIFY(input->hasActiveFocus() == true);
1570 simulateKey(&canvas, Qt::Key_Right);
1571 QVERIFY(input->hasActiveFocus() == false);
1574 simulateKey(&canvas, Qt::Key_Left);
1575 QVERIFY(input->hasActiveFocus() == true);
1577 input->setCursorPosition(input->text().length());
1578 QVERIFY(input->hasActiveFocus() == true);
1581 simulateKey(&canvas, Qt::Key_Left);
1582 QVERIFY(input->hasActiveFocus() == false);
1585 simulateKey(&canvas, Qt::Key_Right);
1586 QVERIFY(input->hasActiveFocus() == true);
1589 void tst_qsgtextinput::copyAndPaste() {
1590 #ifndef QT_NO_CLIPBOARD
1594 PasteboardRef pasteboard;
1595 OSStatus status = PasteboardCreate(0, &pasteboard);
1596 if (status == noErr)
1597 CFRelease(pasteboard);
1599 QSKIP("This machine doesn't support the clipboard", SkipAll);
1603 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1604 QDeclarativeComponent textInputComponent(&engine);
1605 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1606 QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
1607 QVERIFY(textInput != 0);
1610 QCOMPARE(textInput->text().length(), 12);
1611 textInput->select(0, textInput->text().length());;
1613 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1614 QCOMPARE(textInput->selectedText().length(), 12);
1615 textInput->setCursorPosition(0);
1616 QVERIFY(textInput->canPaste());
1618 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1619 QCOMPARE(textInput->text().length(), 24);
1622 QVERIFY(textInput->canPaste());
1623 textInput->setReadOnly(true);
1624 QVERIFY(!textInput->canPaste());
1625 textInput->setReadOnly(false);
1626 QVERIFY(textInput->canPaste());
1629 textInput->setCursorPosition(0);
1630 textInput->selectWord();
1631 QCOMPARE(textInput->selectedText(), QString("Hello"));
1633 // select all and cut
1634 textInput->selectAll();
1636 QCOMPARE(textInput->text().length(), 0);
1638 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1639 QCOMPARE(textInput->text().length(), 24);
1641 // clear copy buffer
1642 QClipboard *clipboard = QApplication::clipboard();
1645 QVERIFY(!textInput->canPaste());
1647 // test that copy functionality is disabled
1648 // when echo mode is set to hide text/password mode
1651 QSGTextInput::EchoMode echoMode = QSGTextInput::EchoMode(index);
1652 textInput->setEchoMode(echoMode);
1653 textInput->setText("My password");
1654 textInput->select(0, textInput->text().length());;
1656 if (echoMode == QSGTextInput::Normal) {
1657 QVERIFY(!clipboard->text().isEmpty());
1658 QCOMPARE(clipboard->text(), QString("My password"));
1661 QVERIFY(clipboard->text().isEmpty());
1670 void tst_qsgtextinput::canPasteEmpty() {
1671 #ifndef QT_NO_CLIPBOARD
1673 QApplication::clipboard()->clear();
1675 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1676 QDeclarativeComponent textInputComponent(&engine);
1677 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1678 QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
1679 QVERIFY(textInput != 0);
1682 bool cp = !lc.isReadOnly() && QApplication::clipboard()->text().length() != 0;
1683 QCOMPARE(textInput->canPaste(), cp);
1688 void tst_qsgtextinput::canPaste() {
1689 #ifndef QT_NO_CLIPBOARD
1691 QApplication::clipboard()->setText("Some text");
1693 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1694 QDeclarativeComponent textInputComponent(&engine);
1695 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1696 QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
1697 QVERIFY(textInput != 0);
1700 bool cp = !lc.isReadOnly() && QApplication::clipboard()->text().length() != 0;
1701 QCOMPARE(textInput->canPaste(), cp);
1706 void tst_qsgtextinput::passwordCharacter()
1708 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
1709 QDeclarativeComponent textInputComponent(&engine);
1710 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1711 QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
1712 QVERIFY(textInput != 0);
1714 textInput->setPasswordCharacter("X");
1715 qreal implicitWidth = textInput->implicitWidth();
1716 textInput->setPasswordCharacter(".");
1718 // QTBUG-12383 content is updated and redrawn
1719 QVERIFY(textInput->implicitWidth() < implicitWidth);
1724 void tst_qsgtextinput::cursorDelegate()
1726 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/cursorTest.qml"));
1728 view.requestActivateWindow();
1729 QSGTextInput *textInputObject = view.rootObject()->findChild<QSGTextInput*>("textInputObject");
1730 QVERIFY(textInputObject != 0);
1731 QVERIFY(textInputObject->findChild<QSGItem*>("cursorInstance"));
1732 //Test Delegate gets created
1733 textInputObject->setFocus(true);
1734 QSGItem* delegateObject = textInputObject->findChild<QSGItem*>("cursorInstance");
1735 QVERIFY(delegateObject);
1736 //Test Delegate gets moved
1737 for(int i=0; i<= textInputObject->text().length(); i++){
1738 textInputObject->setCursorPosition(i);
1739 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1740 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1742 textInputObject->setCursorPosition(0);
1743 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1744 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1745 //Test Delegate gets deleted
1746 textInputObject->setCursorDelegate(0);
1747 QVERIFY(!textInputObject->findChild<QSGItem*>("cursorInstance"));
1750 void tst_qsgtextinput::cursorVisible()
1752 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/cursorVisible.qml"));
1754 view.requestActivateWindow();
1755 QTest::qWaitForWindowShown(&view);
1756 QTRY_COMPARE(view.windowState(), Qt::WindowActive);
1759 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
1761 QCOMPARE(input.isCursorVisible(), false);
1763 input.setCursorVisible(true);
1764 QCOMPARE(input.isCursorVisible(), true);
1765 QCOMPARE(spy.count(), 1);
1767 input.setCursorVisible(false);
1768 QCOMPARE(input.isCursorVisible(), false);
1769 QCOMPARE(spy.count(), 2);
1771 input.setFocus(true);
1772 QCOMPARE(input.isCursorVisible(), false);
1773 QCOMPARE(spy.count(), 2);
1775 input.setParentItem(view.rootObject());
1776 QCOMPARE(input.isCursorVisible(), true);
1777 QCOMPARE(spy.count(), 3);
1779 input.setFocus(false);
1780 QCOMPARE(input.isCursorVisible(), false);
1781 QCOMPARE(spy.count(), 4);
1783 input.setFocus(true);
1784 QCOMPARE(input.isCursorVisible(), true);
1785 QCOMPARE(spy.count(), 5);
1787 view.setWindowState(Qt::WindowNoState);
1788 QCOMPARE(input.isCursorVisible(), false);
1789 QCOMPARE(spy.count(), 6);
1791 view.requestActivateWindow();
1792 QCOMPARE(input.isCursorVisible(), true);
1793 QCOMPARE(spy.count(), 7);
1795 // on mac, setActiveWindow(0) on mac does not deactivate the current application
1796 // (you have to switch to a different app or hide the current app to trigger this)
1797 #if !defined(Q_WS_MAC)
1798 QApplication::setActiveWindow(0);
1799 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(0));
1800 QCOMPARE(input.isCursorVisible(), false);
1801 QCOMPARE(spy.count(), 8);
1803 view.requestActivateWindow();
1804 QTRY_COMPARE(view.windowState(), Qt::WindowActive);
1805 QCOMPARE(input.isCursorVisible(), true);
1806 QCOMPARE(spy.count(), 9);
1810 void tst_qsgtextinput::cursorRectangle()
1812 QString text = "Hello World!";
1815 input.setText(text);
1816 QFontMetricsF fm(input.font());
1817 input.setWidth(fm.width(text.mid(0, 5)));
1821 // some tolerance for different fonts.
1823 const int error = 2;
1825 const int error = 5;
1829 for (int i = 0; i <= 5; ++i) {
1830 input.setCursorPosition(i);
1831 r = input.cursorRectangle();
1832 int textWidth = fm.width(text.mid(0, i));
1834 QVERIFY(r.left() < textWidth + error);
1835 QVERIFY(r.right() > textWidth - error);
1836 QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
1839 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
1840 QVERIFY(r.left() < input.boundingRect().width());
1841 QVERIFY(r.right() >= input.width() - error);
1843 for (int i = 6; i < text.length(); ++i) {
1844 input.setCursorPosition(i);
1845 QCOMPARE(r, input.cursorRectangle());
1846 QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
1849 for (int i = text.length() - 2; i >= 0; --i) {
1850 input.setCursorPosition(i);
1851 r = input.cursorRectangle();
1852 QVERIFY(r.right() >= 0);
1853 QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
1856 input.setText("Hi!");
1857 input.setHAlign(QSGTextInput::AlignRight);
1858 r = input.cursorRectangle();
1859 QVERIFY(r.left() < input.boundingRect().width());
1860 QVERIFY(r.right() >= input.width() - error);
1863 void tst_qsgtextinput::readOnly()
1865 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/readOnly.qml"));
1867 canvas.requestActivateWindow();
1869 QVERIFY(canvas.rootObject() != 0);
1871 QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1873 QVERIFY(input != 0);
1874 QTRY_VERIFY(input->hasActiveFocus() == true);
1875 QVERIFY(input->isReadOnly() == true);
1876 QString initial = input->text();
1877 for(int k=Qt::Key_0; k<=Qt::Key_Z; k++)
1878 simulateKey(&canvas, k);
1879 simulateKey(&canvas, Qt::Key_Return);
1880 simulateKey(&canvas, Qt::Key_Space);
1881 simulateKey(&canvas, Qt::Key_Escape);
1882 QCOMPARE(input->text(), initial);
1884 input->setCursorPosition(3);
1885 input->setReadOnly(false);
1886 QCOMPARE(input->isReadOnly(), false);
1887 QCOMPARE(input->cursorPosition(), input->text().length());
1890 void tst_qsgtextinput::echoMode()
1892 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/echoMode.qml"));
1894 canvas.requestActivateWindow();
1895 QTest::qWaitForWindowShown(&canvas);
1896 QTRY_COMPARE(canvas.windowState(), Qt::WindowActive);
1898 QVERIFY(canvas.rootObject() != 0);
1900 QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1902 QVERIFY(input != 0);
1903 QTRY_VERIFY(input->hasActiveFocus() == true);
1904 QString initial = input->text();
1905 Qt::InputMethodHints ref;
1906 QCOMPARE(initial, QLatin1String("ABCDefgh"));
1907 QCOMPARE(input->echoMode(), QSGTextInput::Normal);
1908 QCOMPARE(input->displayText(), input->text());
1910 ref &= ~Qt::ImhHiddenText;
1911 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1912 QCOMPARE(input->inputMethodHints(), ref);
1913 input->setEchoMode(QSGTextInput::NoEcho);
1914 QCOMPARE(input->text(), initial);
1915 QCOMPARE(input->displayText(), QLatin1String(""));
1916 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
1918 ref |= Qt::ImhHiddenText;
1919 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1920 QCOMPARE(input->inputMethodHints(), ref);
1921 input->setEchoMode(QSGTextInput::Password);
1923 ref |= Qt::ImhHiddenText;
1924 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1925 QCOMPARE(input->text(), initial);
1926 QCOMPARE(input->displayText(), QLatin1String("********"));
1927 QCOMPARE(input->inputMethodHints(), ref);
1928 input->setPasswordCharacter(QChar('Q'));
1929 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
1930 QCOMPARE(input->text(), initial);
1931 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1932 input->setEchoMode(QSGTextInput::PasswordEchoOnEdit);
1933 //PasswordEchoOnEdit
1934 ref &= ~Qt::ImhHiddenText;
1935 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1936 QCOMPARE(input->inputMethodHints(), ref);
1937 QCOMPARE(input->text(), initial);
1938 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1939 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
1940 QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
1941 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1942 QCOMPARE(input->text(), QLatin1String("a"));
1943 QCOMPARE(input->displayText(), QLatin1String("a"));
1944 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
1945 input->setFocus(false);
1946 QVERIFY(input->hasActiveFocus() == false);
1947 QCOMPARE(input->displayText(), QLatin1String("Q"));
1948 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
1949 input->setFocus(true);
1950 QInputMethodEvent inputEvent;
1951 inputEvent.setCommitString(initial);
1952 QApplication::sendEvent(&canvas, &inputEvent);
1953 QCOMPARE(input->text(), initial);
1954 QCOMPARE(input->displayText(), initial);
1955 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
1958 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
1959 void tst_qdeclarativetextinput::passwordEchoDelay()
1961 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/echoMode.qml"));
1964 QApplication::setActiveWindow(&canvas);
1965 QTest::qWaitForWindowShown(&canvas);
1966 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
1968 QVERIFY(canvas.rootObject() != 0);
1970 QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1972 QChar fillChar = QLatin1Char('*');
1974 input->setEchoMode(QDeclarativeTextInput::Password);
1975 QCOMPARE(input->displayText(), QString(8, fillChar));
1976 input->setText(QString());
1977 QCOMPARE(input->displayText(), QString());
1979 QTest::keyPress(&canvas, '0');
1980 QTest::keyPress(&canvas, '1');
1981 QTest::keyPress(&canvas, '2');
1982 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
1983 QTest::keyPress(&canvas, '3');
1984 QTest::keyPress(&canvas, '4');
1985 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1986 QTest::keyPress(&canvas, Qt::Key_Backspace);
1987 QCOMPARE(input->displayText(), QString(4, fillChar));
1988 QTest::keyPress(&canvas, '4');
1989 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1990 QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
1991 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
1992 QTest::keyPress(&canvas, '5');
1993 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
1994 input->setFocus(false);
1995 QVERIFY(!input->hasFocus());
1996 QCOMPARE(input->displayText(), QString(6, fillChar));
1997 input->setFocus(true);
1998 QTRY_VERIFY(input->hasFocus());
1999 QCOMPARE(input->displayText(), QString(6, fillChar));
2000 QTest::keyPress(&canvas, '6');
2001 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2003 QInputMethodEvent ev;
2004 ev.setCommitString(QLatin1String("7"));
2005 QApplication::sendEvent(&canvas, &ev);
2006 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2011 void tst_qsgtextinput::simulateKey(QSGView *view, int key)
2013 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2014 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2016 QApplication::sendEvent(view, &press);
2017 QApplication::sendEvent(view, &release);
2020 class MyInputContext : public QInputContext
2023 MyInputContext() : openInputPanelReceived(false), closeInputPanelReceived(false), updateReceived(false), eventType(QEvent::None) {}
2024 ~MyInputContext() {}
2026 QString identifierName() { return QString(); }
2027 QString language() { return QString(); }
2031 bool isComposing() const { return false; }
2033 bool filterEvent( const QEvent *event )
2035 if (event->type() == QEvent::RequestSoftwareInputPanel)
2036 openInputPanelReceived = true;
2037 if (event->type() == QEvent::CloseSoftwareInputPanel)
2038 closeInputPanelReceived = true;
2039 return QInputContext::filterEvent(event);
2042 void update() { updateReceived = true; }
2044 void mouseHandler(int x, QMouseEvent *event)
2047 eventType = event->type();
2048 eventPosition = event->pos();
2049 eventGlobalPosition = event->globalPos();
2050 eventButton = event->button();
2051 eventButtons = event->buttons();
2052 eventModifiers = event->modifiers();
2055 void sendPreeditText(const QString &text, int cursor)
2057 QList<QInputMethodEvent::Attribute> attributes;
2058 attributes.append(QInputMethodEvent::Attribute(
2059 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2061 QInputMethodEvent event(text, attributes);
2065 bool openInputPanelReceived;
2066 bool closeInputPanelReceived;
2067 bool updateReceived;
2069 QEvent::Type eventType;
2070 QPoint eventPosition;
2071 QPoint eventGlobalPosition;
2072 Qt::MouseButton eventButton;
2073 Qt::MouseButtons eventButtons;
2074 Qt::KeyboardModifiers eventModifiers;
2077 void tst_qsgtextinput::openInputPanelOnClick()
2079 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/openInputPanel.qml"));
2081 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
2082 // and QWidget won't allow an input context to be set when the flag is not set.
2083 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2084 view.setInputContext(&ic);
2085 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2087 QApplication::setActiveWindow(&view);
2088 QTest::qWaitForWindowShown(&view);
2089 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2090 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2093 QSGItemPrivate* pri = QSGItemPrivate::get(input);
2094 QSGTextInputPrivate *inputPrivate = static_cast<QSGTextInputPrivate*>(pri);
2096 // input panel on click
2097 inputPrivate->showInputPanelOnFocus = false;
2099 QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(
2100 view.style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
2101 QTest::mouseClick(&view, Qt::LeftButton, 0, input->pos().toPoint());
2102 QApplication::processEvents();
2103 if (behavior == QStyle::RSIP_OnMouseClickAndAlreadyFocused) {
2104 QCOMPARE(ic.openInputPanelReceived, false);
2105 QTest::mouseClick(&view, Qt::LeftButton, 0, input->pos().toPoint());
2106 QApplication::processEvents();
2107 QCOMPARE(ic.openInputPanelReceived, true);
2108 } else if (behavior == QStyle::RSIP_OnMouseClick) {
2109 QCOMPARE(ic.openInputPanelReceived, true);
2111 ic.openInputPanelReceived = false;
2113 // focus should not cause input panels to open or close
2114 input->setFocus(false);
2115 input->setFocus(true);
2116 input->setFocus(false);
2117 input->setFocus(true);
2118 input->setFocus(false);
2119 QCOMPARE(ic.openInputPanelReceived, false);
2120 QCOMPARE(ic.closeInputPanelReceived, false);
2123 void tst_qsgtextinput::openInputPanelOnFocus()
2125 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/openInputPanel.qml"));
2127 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
2128 // and QWidget won't allow an input context to be set when the flag is not set.
2129 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2130 view.setInputContext(&ic);
2131 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2133 QApplication::setActiveWindow(&view);
2134 QTest::qWaitForWindowShown(&view);
2135 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2136 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2138 QSignalSpy focusOnPressSpy(input, SIGNAL(activeFocusOnPressChanged(bool)));
2140 QSGItemPrivate* pri = QSGItemPrivate::get(input);
2141 QSGTextInputPrivate *inputPrivate = static_cast<QSGTextInputPrivate*>(pri);
2142 inputPrivate->showInputPanelOnFocus = true;
2144 // test default values
2145 QVERIFY(input->focusOnPress());
2146 QCOMPARE(ic.openInputPanelReceived, false);
2147 QCOMPARE(ic.closeInputPanelReceived, false);
2149 // focus on press, input panel on focus
2150 QTest::mousePress(&view, Qt::LeftButton, 0, input->pos().toPoint());
2151 QApplication::processEvents();
2152 QVERIFY(input->hasActiveFocus());
2153 QCOMPARE(ic.openInputPanelReceived, true);
2154 ic.openInputPanelReceived = false;
2156 // no events on release
2157 QTest::mouseRelease(&view, Qt::LeftButton, 0, input->pos().toPoint());
2158 QCOMPARE(ic.openInputPanelReceived, false);
2159 ic.openInputPanelReceived = false;
2161 // if already focused, input panel can be opened on press
2162 QVERIFY(input->hasActiveFocus());
2163 QTest::mousePress(&view, Qt::LeftButton, 0, input->pos().toPoint());
2164 QApplication::processEvents();
2165 QCOMPARE(ic.openInputPanelReceived, true);
2166 ic.openInputPanelReceived = false;
2168 // input method should stay enabled if focus
2169 // is lost to an item that also accepts inputs
2170 QSGTextInput anotherInput;
2171 anotherInput.setParentItem(view.rootItem());
2172 anotherInput.setFocus(true);
2173 QApplication::processEvents();
2174 QCOMPARE(ic.openInputPanelReceived, true);
2175 ic.openInputPanelReceived = false;
2176 QCOMPARE(view.inputContext(), (QInputContext*)&ic);
2177 QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled));
2179 // input method should be disabled if focus
2180 // is lost to an item that doesn't accept inputs
2182 item.setParentItem(view.rootItem());
2183 item.setFocus(true);
2184 QApplication::processEvents();
2185 QCOMPARE(ic.openInputPanelReceived, false);
2186 QVERIFY(view.inputContext() == 0);
2187 QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
2189 // no automatic input panel events should
2190 // be sent if activeFocusOnPress is false
2191 input->setFocusOnPress(false);
2192 QCOMPARE(focusOnPressSpy.count(),1);
2193 input->setFocusOnPress(false);
2194 QCOMPARE(focusOnPressSpy.count(),1);
2195 input->setFocus(false);
2196 input->setFocus(true);
2197 QTest::mousePress(&view, Qt::LeftButton, 0, input->pos().toPoint());
2198 QTest::mouseRelease(&view, Qt::LeftButton, 0, input->pos().toPoint());
2199 QApplication::processEvents();
2200 QCOMPARE(ic.openInputPanelReceived, false);
2201 QCOMPARE(ic.closeInputPanelReceived, false);
2203 // one show input panel event should
2204 // be set when openSoftwareInputPanel is called
2205 input->openSoftwareInputPanel();
2206 QCOMPARE(ic.openInputPanelReceived, true);
2207 QCOMPARE(ic.closeInputPanelReceived, false);
2208 ic.openInputPanelReceived = false;
2210 // one close input panel event should
2211 // be sent when closeSoftwareInputPanel is called
2212 input->closeSoftwareInputPanel();
2213 QCOMPARE(ic.openInputPanelReceived, false);
2214 QCOMPARE(ic.closeInputPanelReceived, true);
2215 ic.closeInputPanelReceived = false;
2217 // set activeFocusOnPress back to true
2218 input->setFocusOnPress(true);
2219 QCOMPARE(focusOnPressSpy.count(),2);
2220 input->setFocusOnPress(true);
2221 QCOMPARE(focusOnPressSpy.count(),2);
2222 input->setFocus(false);
2223 QApplication::processEvents();
2224 QCOMPARE(ic.openInputPanelReceived, false);
2225 QCOMPARE(ic.closeInputPanelReceived, false);
2226 ic.closeInputPanelReceived = false;
2228 // input panel should not re-open
2229 // if focus has already been set
2230 input->setFocus(true);
2231 QCOMPARE(ic.openInputPanelReceived, true);
2232 ic.openInputPanelReceived = false;
2233 input->setFocus(true);
2234 QCOMPARE(ic.openInputPanelReceived, false);
2236 // input method should be disabled
2237 // if TextInput loses focus
2238 input->setFocus(false);
2239 QApplication::processEvents();
2240 QVERIFY(view.inputContext() == 0);
2241 QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
2243 // input method should not be enabled
2244 // if TextEdit is read only.
2245 input->setReadOnly(true);
2246 ic.openInputPanelReceived = false;
2247 input->setFocus(true);
2248 QApplication::processEvents();
2249 QCOMPARE(ic.openInputPanelReceived, false);
2250 QVERIFY(view.inputContext() == 0);
2251 QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
2254 class MyTextInput : public QSGTextInput
2257 MyTextInput(QSGItem *parent = 0) : QSGTextInput(parent)
2261 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2264 return QSGTextInput::updatePaintNode(node, data);
2269 void tst_qsgtextinput::setHAlignClearCache()
2273 input.setText("Hello world");
2274 input.setParentItem(view.rootItem());
2276 QApplication::setActiveWindow(&view);
2277 QTest::qWaitForWindowShown(&view);
2278 QTRY_COMPARE(input.nbPaint, 1);
2279 input.setHAlign(QSGTextInput::AlignRight);
2280 //Changing the alignment should trigger a repaint
2281 QTRY_COMPARE(input.nbPaint, 2);
2284 void tst_qsgtextinput::focusOutClearSelection()
2288 QSGTextInput input2;
2289 input.setText(QLatin1String("Hello world"));
2290 input.setFocus(true);
2291 input2.setParentItem(view.rootItem());
2292 input.setParentItem(view.rootItem());
2294 QApplication::setActiveWindow(&view);
2295 QTest::qWaitForWindowShown(&view);
2297 //The selection should work
2298 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2299 input2.setFocus(true);
2300 QApplication::processEvents();
2301 //The input lost the focus selection should be cleared
2302 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2305 void tst_qsgtextinput::geometrySignals()
2307 QDeclarativeComponent component(&engine, SRCDIR "/data/geometrySignals.qml");
2308 QObject *o = component.create();
2310 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2311 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2315 void tst_qsgtextinput::testQtQuick11Attributes()
2317 QFETCH(QString, code);
2318 QFETCH(QString, warning);
2319 QFETCH(QString, error);
2321 QDeclarativeEngine engine;
2324 QDeclarativeComponent valid(&engine);
2325 valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2326 obj = valid.create();
2328 QVERIFY(valid.errorString().isEmpty());
2331 QDeclarativeComponent invalid(&engine);
2332 invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2333 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2334 obj = invalid.create();
2335 QCOMPARE(invalid.errorString(), error);
2339 void tst_qsgtextinput::testQtQuick11Attributes_data()
2341 QTest::addColumn<QString>("code");
2342 QTest::addColumn<QString>("warning");
2343 QTest::addColumn<QString>("error");
2345 QTest::newRow("canPaste") << "property bool foo: canPaste"
2346 << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2349 QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2350 << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2353 QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2354 << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2358 void tst_qsgtextinput::preeditAutoScroll()
2360 QString preeditText = "califragisiticexpialidocious!";
2362 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/preeditAutoScroll.qml"));
2364 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2365 // and QWidget won't allow an input context to be set when the flag is not set.
2366 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2367 view.setInputContext(&ic);
2368 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2370 QApplication::setActiveWindow(&view);
2371 QTest::qWaitForWindowShown(&view);
2372 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2373 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2376 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2377 int cursorRectangleChanges = 0;
2379 QFontMetricsF fm(input->font());
2380 input->setWidth(fm.width(input->text()));
2382 // test the text is scrolled so the preedit is visible.
2383 ic.sendPreeditText(preeditText.mid(0, 3), 1);
2385 QEXPECT_FAIL("", "QTBUG-21011 fails", Abort);
2387 QVERIFY(input->positionAt(0) != 0);
2388 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2389 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2391 // test the text is scrolled back when the preedit is removed.
2392 ic.sendEvent(QInputMethodEvent());
2393 QCOMPARE(input->positionAt(0), 0);
2394 QCOMPARE(input->positionAt(input->width()), 5);
2395 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2397 // some tolerance for different fonts.
2399 const int error = 2;
2401 const int error = 5;
2404 // test if the preedit is larger than the text input that the
2405 // character preceding the cursor is still visible.
2406 qreal x = input->positionToRectangle(0).x();
2407 for (int i = 0; i < 3; ++i) {
2408 ic.sendPreeditText(preeditText, i + 1);
2409 QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2410 QVERIFY(input->positionToRectangle(0).x() < x);
2411 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2412 x = input->positionToRectangle(0).x();
2414 for (int i = 1; i >= 0; --i) {
2415 ic.sendPreeditText(preeditText, i + 1);
2416 QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2417 QVERIFY(input->positionToRectangle(0).x() > x);
2418 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2419 x = input->positionToRectangle(0).x();
2422 // Test incrementing the preedit cursor doesn't cause further
2423 // scrolling when right most text is visible.
2424 ic.sendPreeditText(preeditText, preeditText.length() - 3);
2425 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2426 x = input->positionToRectangle(0).x();
2427 for (int i = 2; i >= 0; --i) {
2428 ic.sendPreeditText(preeditText, preeditText.length() - i);
2429 QCOMPARE(input->positionToRectangle(0).x(), x);
2430 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2432 for (int i = 1; i < 3; ++i) {
2433 ic.sendPreeditText(preeditText, preeditText.length() - i);
2434 QCOMPARE(input->positionToRectangle(0).x(), x);
2435 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2438 // Test disabling auto scroll.
2439 ic.sendEvent(QInputMethodEvent());
2441 input->setAutoScroll(false);
2442 ic.sendPreeditText(preeditText.mid(0, 3), 1);
2443 QCOMPARE(input->positionAt(0), 0);
2444 QCOMPARE(input->positionAt(input->width()), 5);
2447 void tst_qsgtextinput::preeditMicroFocus()
2449 QString preeditText = "super";
2451 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputMethodEvent.qml"));
2453 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2454 // and QWidget won't allow an input context to be set when the flag is not set.
2455 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2456 view.setInputContext(&ic);
2457 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2459 QApplication::setActiveWindow(&view);
2460 QTest::qWaitForWindowShown(&view);
2461 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2462 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2466 QRect previousRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
2468 // Verify that the micro focus rect is positioned the same for position 0 as
2469 // it would be if there was no preedit text.
2470 ic.updateReceived = false;
2471 ic.sendPreeditText(preeditText, 0);
2472 currentRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
2473 QCOMPARE(currentRect, previousRect);
2474 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2475 QCOMPARE(ic.updateReceived, true);
2478 // Verify that the micro focus rect moves to the left as the cursor position
2480 for (int i = 1; i <= 5; ++i) {
2481 ic.updateReceived = false;
2482 ic.sendPreeditText(preeditText, i);
2483 currentRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
2484 QVERIFY(previousRect.left() < currentRect.left());
2485 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2486 QCOMPARE(ic.updateReceived, true);
2488 previousRect = currentRect;
2491 // Verify that if there is no preedit cursor then the micro focus rect is the
2492 // same as it would be if it were positioned at the end of the preedit text.
2493 ic.sendPreeditText(preeditText, 0);
2494 ic.updateReceived = false;
2495 ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2496 currentRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
2497 QCOMPARE(currentRect, previousRect);
2498 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2499 QCOMPARE(ic.updateReceived, true);
2503 void tst_qsgtextinput::inputContextMouseHandler()
2505 QString text = "supercalifragisiticexpialidocious!";
2507 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
2509 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2510 // and QWidget won't allow an input context to be set when the flag is not set.
2511 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2512 view.setInputContext(&ic);
2513 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2515 QApplication::setActiveWindow(&view);
2516 QTest::qWaitForWindowShown(&view);
2517 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2518 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2521 QFontMetricsF fm(input->font());
2522 const qreal y = fm.height() / 2;
2524 QPoint position2 = input->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
2525 QPoint position8 = input->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
2526 QPoint position20 = input->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
2527 QPoint position27 = input->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
2528 QPoint globalPosition2 = view.mapToGlobal(position2);
2529 QPoint globalposition8 = view.mapToGlobal(position8);
2530 QPoint globalposition20 = view.mapToGlobal(position20);
2531 QPoint globalposition27 = view.mapToGlobal(position27);
2533 ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2535 QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
2536 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2537 QCOMPARE(ic.eventPosition, position2);
2538 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2539 QCOMPARE(ic.eventButton, Qt::LeftButton);
2540 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2541 QVERIFY(ic.cursor < 0);
2542 ic.eventType = QEvent::None;
2544 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
2545 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2546 QCOMPARE(ic.eventPosition, position2);
2547 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2548 QCOMPARE(ic.eventButton, Qt::LeftButton);
2549 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2550 QVERIFY(ic.cursor < 0);
2551 ic.eventType = QEvent::None;
2553 { QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2554 QApplication::sendEvent(&view, &mv); }
2555 QCOMPARE(ic.eventType, QEvent::None);
2557 { QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2558 QApplication::sendEvent(&view, &mv); }
2559 QCOMPARE(ic.eventType, QEvent::MouseMove);
2560 QCOMPARE(ic.eventPosition, position27);
2561 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2562 QCOMPARE(ic.eventButton, Qt::LeftButton);
2563 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2564 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); // 15 is expected but some platforms may be off by one.
2565 ic.eventType = QEvent::None;
2567 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
2568 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2569 QCOMPARE(ic.eventPosition, position27);
2570 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2571 QCOMPARE(ic.eventButton, Qt::LeftButton);
2572 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2573 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2574 ic.eventType = QEvent::None;
2576 // And in the other direction.
2577 QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
2578 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2579 QCOMPARE(ic.eventPosition, position27);
2580 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2581 QCOMPARE(ic.eventButton, Qt::LeftButton);
2582 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2583 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2584 ic.eventType = QEvent::None;
2586 QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
2587 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2588 QCOMPARE(ic.eventPosition, position27);
2589 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2590 QCOMPARE(ic.eventButton, Qt::RightButton);
2591 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2592 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2593 ic.eventType = QEvent::None;
2595 { QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2596 QApplication::sendEvent(&view, &mv); }
2597 QCOMPARE(ic.eventType, QEvent::MouseMove);
2598 QCOMPARE(ic.eventPosition, position20);
2599 QCOMPARE(ic.eventGlobalPosition, globalposition20);
2600 QCOMPARE(ic.eventButton, Qt::RightButton);
2601 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2602 QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2603 ic.eventType = QEvent::None;
2605 { QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2606 QApplication::sendEvent(&view, &mv); }
2607 QCOMPARE(ic.eventType, QEvent::None);
2609 QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
2610 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2611 QCOMPARE(ic.eventPosition, position2);
2612 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2613 QCOMPARE(ic.eventButton, Qt::RightButton);
2614 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2615 QVERIFY(ic.cursor < 0);
2616 ic.eventType = QEvent::None;
2619 void tst_qsgtextinput::inputMethodComposing()
2621 QString text = "supercalifragisiticexpialidocious!";
2623 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
2625 QApplication::setActiveWindow(&view);
2626 QTest::qWaitForWindowShown(&view);
2627 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2628 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2630 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2632 QCOMPARE(input->isInputMethodComposing(), false);
2634 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2635 QApplication::sendEvent(&view, &event);
2637 QCOMPARE(input->isInputMethodComposing(), true);
2638 QCOMPARE(spy.count(), 1);
2641 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2642 QApplication::sendEvent(&view, &event);
2644 QCOMPARE(spy.count(), 1);
2647 QInputMethodEvent event;
2648 QApplication::sendEvent(&view, &event);
2650 QCOMPARE(input->isInputMethodComposing(), false);
2651 QCOMPARE(spy.count(), 2);
2654 void tst_qsgtextinput::cursorRectangleSize()
2656 QSGView *canvas = new QSGView(QUrl::fromLocalFile(SRCDIR "/data/positionAt.qml"));
2657 QVERIFY(canvas->rootObject() != 0);
2660 QApplication::setActiveWindow(canvas);
2661 QTest::qWaitForWindowShown(canvas);
2663 QSGTextInput *textInput = qobject_cast<QSGTextInput *>(canvas->rootObject());
2664 QVERIFY(textInput != 0);
2665 textInput->setFocus(Qt::OtherFocusReason);
2666 QRectF cursorRect = textInput->positionToRectangle(textInput->cursorPosition());
2667 QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImMicroFocus).toRectF();
2668 QRectF microFocusFromApp= QApplication::focusWidget()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
2670 QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2671 QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2676 QTEST_MAIN(tst_qsgtextinput)
2678 #include "tst_qsgtextinput.moc"