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/qquickview.h>
47 #include <QtGui/qguiapplication.h>
48 #include <QInputPanel>
49 #include <private/qquicktextinput_p.h>
50 #include <private/qquicktextinput_p_p.h>
54 #include <QInputContext>
55 #include <private/qsgdistancefieldglyphcache_p.h>
56 #include <QtOpenGL/QGLShaderProgram>
59 #include "qplatformdefs.h"
61 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
62 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
65 #define QTBUG_21691_MESSAGE "QTBUG-21691: The test needs to be rewritten to not use QInputContext"
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 = TESTDATA("");
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_qquicktextinput : public QObject
89 tst_qquicktextinput();
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();
108 void tripleClickSelectsAll();
110 void horizontalAlignment_data();
111 void horizontalAlignment();
112 void horizontalAlignment_RightToLeft();
121 void passwordCharacter();
122 void cursorDelegate();
123 void cursorVisible();
124 void cursorRectangle();
126 void navigation_RTL();
128 void canPasteEmpty();
132 void openInputPanel();
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 QTBUG_19956_data();
152 void QTBUG_19956_regexp();
155 void simulateKey(QQuickView *, int key);
157 QDeclarativeEngine engine;
158 QStringList standard;
159 QStringList colorStrings;
161 void tst_qquicktextinput::initTestCase()
165 void tst_qquicktextinput::cleanupTestCase()
169 tst_qquicktextinput::tst_qquicktextinput()
171 standard << "the quick brown fox jumped over the lazy dog"
172 << "It's supercalifragisiticexpialidocious!"
177 colorStrings << "aliceblue"
191 void tst_qquicktextinput::text()
194 QDeclarativeComponent textinputComponent(&engine);
195 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
196 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
198 QVERIFY(textinputObject != 0);
199 QCOMPARE(textinputObject->text(), QString(""));
201 delete textinputObject;
204 for (int i = 0; i < standard.size(); i++)
206 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
207 QDeclarativeComponent textinputComponent(&engine);
208 textinputComponent.setData(componentStr.toLatin1(), QUrl());
209 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
211 QVERIFY(textinputObject != 0);
212 QCOMPARE(textinputObject->text(), standard.at(i));
214 delete textinputObject;
219 void tst_qquicktextinput::width()
221 // uses Font metrics to find the width for standard
223 QDeclarativeComponent textinputComponent(&engine);
224 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
225 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
227 QVERIFY(textinputObject != 0);
228 QCOMPARE(textinputObject->width(), 0.0);
230 delete textinputObject;
233 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
235 for (int i = 0; i < standard.size(); i++)
238 qreal metricWidth = 0.0;
239 if (requiresUnhintedMetrics) {
240 QString s = standard.at(i);
241 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
243 QTextLayout layout(s);
244 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
247 option.setUseDesignMetrics(true);
248 layout.setTextOption(option);
251 layout.beginLayout();
253 QTextLine line = layout.createLine();
260 metricWidth = ceil(layout.boundingRect().width());
263 metricWidth = fm.width(standard.at(i));
266 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
267 QDeclarativeComponent textinputComponent(&engine);
268 textinputComponent.setData(componentStr.toLatin1(), QUrl());
269 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
271 QVERIFY(textinputObject != 0);
272 int delta = abs(int(int(textinputObject->width()) - metricWidth));
273 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
275 delete textinputObject;
279 void tst_qquicktextinput::font()
281 //test size, then bold, then italic, then family
283 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
284 QDeclarativeComponent textinputComponent(&engine);
285 textinputComponent.setData(componentStr.toLatin1(), QUrl());
286 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
288 QVERIFY(textinputObject != 0);
289 QCOMPARE(textinputObject->font().pointSize(), 40);
290 QCOMPARE(textinputObject->font().bold(), false);
291 QCOMPARE(textinputObject->font().italic(), false);
293 delete textinputObject;
297 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
298 QDeclarativeComponent textinputComponent(&engine);
299 textinputComponent.setData(componentStr.toLatin1(), QUrl());
300 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
302 QVERIFY(textinputObject != 0);
303 QCOMPARE(textinputObject->font().bold(), true);
304 QCOMPARE(textinputObject->font().italic(), false);
306 delete textinputObject;
310 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
311 QDeclarativeComponent textinputComponent(&engine);
312 textinputComponent.setData(componentStr.toLatin1(), QUrl());
313 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
315 QVERIFY(textinputObject != 0);
316 QCOMPARE(textinputObject->font().italic(), true);
317 QCOMPARE(textinputObject->font().bold(), false);
319 delete textinputObject;
323 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
324 QDeclarativeComponent textinputComponent(&engine);
325 textinputComponent.setData(componentStr.toLatin1(), QUrl());
326 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
328 QVERIFY(textinputObject != 0);
329 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
330 QCOMPARE(textinputObject->font().bold(), false);
331 QCOMPARE(textinputObject->font().italic(), false);
333 delete textinputObject;
337 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
338 QDeclarativeComponent textinputComponent(&engine);
339 textinputComponent.setData(componentStr.toLatin1(), QUrl());
340 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
342 QVERIFY(textinputObject != 0);
343 QCOMPARE(textinputObject->font().family(), QString(""));
345 delete textinputObject;
349 void tst_qquicktextinput::color()
352 for (int i = 0; i < colorStrings.size(); i++)
354 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
355 QDeclarativeComponent textinputComponent(&engine);
356 textinputComponent.setData(componentStr.toLatin1(), QUrl());
357 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
358 QVERIFY(textinputObject != 0);
359 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
361 delete textinputObject;
364 //test selection color
365 for (int i = 0; i < colorStrings.size(); i++)
367 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
368 QDeclarativeComponent textinputComponent(&engine);
369 textinputComponent.setData(componentStr.toLatin1(), QUrl());
370 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
371 QVERIFY(textinputObject != 0);
372 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
374 delete textinputObject;
377 //test selected text color
378 for (int i = 0; i < colorStrings.size(); i++)
380 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
381 QDeclarativeComponent textinputComponent(&engine);
382 textinputComponent.setData(componentStr.toLatin1(), QUrl());
383 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
384 QVERIFY(textinputObject != 0);
385 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
387 delete textinputObject;
391 QString colorStr = "#AA001234";
392 QColor testColor("#001234");
393 testColor.setAlpha(170);
395 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
396 QDeclarativeComponent textinputComponent(&engine);
397 textinputComponent.setData(componentStr.toLatin1(), QUrl());
398 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
400 QVERIFY(textinputObject != 0);
401 QCOMPARE(textinputObject->color(), testColor);
403 delete textinputObject;
407 void tst_qquicktextinput::selection()
409 QString testStr = standard[0];
410 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
411 QDeclarativeComponent textinputComponent(&engine);
412 textinputComponent.setData(componentStr.toLatin1(), QUrl());
413 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
414 QVERIFY(textinputObject != 0);
417 //Test selection follows cursor
418 for (int i=0; i<= testStr.size(); i++) {
419 textinputObject->setCursorPosition(i);
420 QCOMPARE(textinputObject->cursorPosition(), i);
421 QCOMPARE(textinputObject->selectionStart(), i);
422 QCOMPARE(textinputObject->selectionEnd(), i);
423 QVERIFY(textinputObject->selectedText().isNull());
426 textinputObject->setCursorPosition(0);
427 QVERIFY(textinputObject->cursorPosition() == 0);
428 QVERIFY(textinputObject->selectionStart() == 0);
429 QVERIFY(textinputObject->selectionEnd() == 0);
430 QVERIFY(textinputObject->selectedText().isNull());
432 // Verify invalid positions are ignored.
433 textinputObject->setCursorPosition(-1);
434 QVERIFY(textinputObject->cursorPosition() == 0);
435 QVERIFY(textinputObject->selectionStart() == 0);
436 QVERIFY(textinputObject->selectionEnd() == 0);
437 QVERIFY(textinputObject->selectedText().isNull());
439 textinputObject->setCursorPosition(textinputObject->text().count()+1);
440 QVERIFY(textinputObject->cursorPosition() == 0);
441 QVERIFY(textinputObject->selectionStart() == 0);
442 QVERIFY(textinputObject->selectionEnd() == 0);
443 QVERIFY(textinputObject->selectedText().isNull());
446 for (int i=0; i<= testStr.size(); i++) {
447 textinputObject->select(0,i);
448 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
450 for (int i=0; i<= testStr.size(); i++) {
451 textinputObject->select(i,testStr.size());
452 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
455 textinputObject->setCursorPosition(0);
456 QVERIFY(textinputObject->cursorPosition() == 0);
457 QVERIFY(textinputObject->selectionStart() == 0);
458 QVERIFY(textinputObject->selectionEnd() == 0);
459 QVERIFY(textinputObject->selectedText().isNull());
461 //Test Error Ignoring behaviour
462 textinputObject->setCursorPosition(0);
463 QVERIFY(textinputObject->selectedText().isNull());
464 textinputObject->select(-10,0);
465 QVERIFY(textinputObject->selectedText().isNull());
466 textinputObject->select(100,110);
467 QVERIFY(textinputObject->selectedText().isNull());
468 textinputObject->select(0,-10);
469 QVERIFY(textinputObject->selectedText().isNull());
470 textinputObject->select(0,100);
471 QVERIFY(textinputObject->selectedText().isNull());
472 textinputObject->select(0,10);
473 QVERIFY(textinputObject->selectedText().size() == 10);
474 textinputObject->select(-10,10);
475 QVERIFY(textinputObject->selectedText().size() == 10);
476 textinputObject->select(100,101);
477 QVERIFY(textinputObject->selectedText().size() == 10);
478 textinputObject->select(0,-10);
479 QVERIFY(textinputObject->selectedText().size() == 10);
480 textinputObject->select(0,100);
481 QVERIFY(textinputObject->selectedText().size() == 10);
483 textinputObject->deselect();
484 QVERIFY(textinputObject->selectedText().isNull());
485 textinputObject->select(0,10);
486 QVERIFY(textinputObject->selectedText().size() == 10);
487 textinputObject->deselect();
488 QVERIFY(textinputObject->selectedText().isNull());
490 delete textinputObject;
493 void tst_qquicktextinput::isRightToLeft_data()
495 QTest::addColumn<QString>("text");
496 QTest::addColumn<bool>("emptyString");
497 QTest::addColumn<bool>("firstCharacter");
498 QTest::addColumn<bool>("lastCharacter");
499 QTest::addColumn<bool>("middleCharacter");
500 QTest::addColumn<bool>("startString");
501 QTest::addColumn<bool>("midString");
502 QTest::addColumn<bool>("endString");
504 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
505 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
506 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
507 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
508 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
509 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;
510 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
513 void tst_qquicktextinput::isRightToLeft()
515 QFETCH(QString, text);
516 QFETCH(bool, emptyString);
517 QFETCH(bool, firstCharacter);
518 QFETCH(bool, lastCharacter);
519 QFETCH(bool, middleCharacter);
520 QFETCH(bool, startString);
521 QFETCH(bool, midString);
522 QFETCH(bool, endString);
524 QQuickTextInput textInput;
525 textInput.setText(text);
527 // first test that the right string is delivered to the QString::isRightToLeft()
528 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
529 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
530 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
531 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
532 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
533 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
535 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
536 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
538 // then test that the feature actually works
539 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
540 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
541 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
542 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
543 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
544 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
546 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
547 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
550 void tst_qquicktextinput::moveCursorSelection_data()
552 QTest::addColumn<QString>("testStr");
553 QTest::addColumn<int>("cursorPosition");
554 QTest::addColumn<int>("movePosition");
555 QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
556 QTest::addColumn<int>("selectionStart");
557 QTest::addColumn<int>("selectionEnd");
558 QTest::addColumn<bool>("reversible");
560 // () contains the text selected by the cursor.
561 // <> contains the actual selection.
563 QTest::newRow("(t)he|characters")
564 << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
565 QTest::newRow("do(g)|characters")
566 << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
567 QTest::newRow("jum(p)ed|characters")
568 << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
569 QTest::newRow("jumped( )over|characters")
570 << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
571 QTest::newRow("(the )|characters")
572 << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
573 QTest::newRow("( dog)|characters")
574 << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
575 QTest::newRow("( jumped )|characters")
576 << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
577 QTest::newRow("th(e qu)ick|characters")
578 << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
579 QTest::newRow("la(zy d)og|characters")
580 << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
581 QTest::newRow("jum(ped ov)er|characters")
582 << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
583 QTest::newRow("()the|characters")
584 << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
585 QTest::newRow("dog()|characters")
586 << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
587 QTest::newRow("jum()ped|characters")
588 << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
590 QTest::newRow("<(t)he>|words")
591 << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
592 QTest::newRow("<do(g)>|words")
593 << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
594 QTest::newRow("<jum(p)ed>|words")
595 << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
596 QTest::newRow("<jumped( )>over|words,ltr")
597 << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
598 QTest::newRow("jumped<( )over>|words,rtl")
599 << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
600 QTest::newRow("<(the )>quick|words,ltr")
601 << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
602 QTest::newRow("<(the )quick>|words,rtl")
603 << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
604 QTest::newRow("<lazy( dog)>|words,ltr")
605 << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
606 QTest::newRow("lazy<( dog)>|words,rtl")
607 << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
608 QTest::newRow("<fox( jumped )>over|words,ltr")
609 << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
610 QTest::newRow("fox<( jumped )over>|words,rtl")
611 << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
612 QTest::newRow("<th(e qu)ick>|words")
613 << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
614 QTest::newRow("<la(zy d)og|words>")
615 << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
616 QTest::newRow("<jum(ped ov)er>|words")
617 << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
618 QTest::newRow("<()>the|words")
619 << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
620 QTest::newRow("dog<()>|words")
621 << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
622 QTest::newRow("jum<()>ped|words")
623 << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
625 QTest::newRow("Hello<(,)> |words")
626 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
627 QTest::newRow("Hello<(, )>world|words,ltr")
628 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
629 QTest::newRow("Hello<(, )world>|words,rtl")
630 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
631 QTest::newRow("<Hel(lo, )>world|words,ltr")
632 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
633 QTest::newRow("<Hel(lo, )world>|words,rtl")
634 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
635 QTest::newRow("<Hel(lo)>,|words")
636 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
637 QTest::newRow("Hello<()>,|words")
638 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
639 QTest::newRow("Hello,<()>|words")
640 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
641 QTest::newRow("Hello<,( )>world|words,ltr")
642 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
643 QTest::newRow("Hello,<( )world>|words,rtl")
644 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
645 QTest::newRow("Hello<,( world)>|words,ltr")
646 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
647 QTest::newRow("Hello,<( world)>|words,rtl")
648 << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
649 QTest::newRow("Hello<,( world!)>|words,ltr")
650 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
651 QTest::newRow("Hello,<( world!)>|words,rtl")
652 << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
653 QTest::newRow("Hello<(, world!)>|words")
654 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
655 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
657 // QTest::newRow("world<(!)>|words")
658 // << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
659 QTest::newRow("world!<()>)|words")
660 << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
661 QTest::newRow("world<()>!)|words")
662 << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
664 QTest::newRow("<(,)>olleH |words")
665 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
666 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
667 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
668 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
669 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
670 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
671 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
672 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
673 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
674 QTest::newRow(",<(ol)leH>,|words")
675 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
676 QTest::newRow(",<()>olleH|words")
677 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
678 QTest::newRow("<()>,olleH|words")
679 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
680 QTest::newRow("<dlrow( )>,olleH|words,ltr")
681 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
682 QTest::newRow("dlrow<( ),>olleH|words,rtl")
683 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
684 QTest::newRow("<(dlrow )>,olleH|words,ltr")
685 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
686 QTest::newRow("<(dlrow ),>olleH|words,rtl")
687 << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
688 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
689 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
690 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
691 << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
692 QTest::newRow("(!dlrow ,)olleH|words")
693 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
694 QTest::newRow("<(!)>dlrow|words")
695 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
696 QTest::newRow("<()>!dlrow|words")
697 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
698 QTest::newRow("!<()>dlrow|words")
699 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
701 QTest::newRow(" <s(pac)ey> text |words")
702 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
703 QTest::newRow(" spacey <t(ex)t> |words")
704 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
705 QTest::newRow("<( )>spacey text |words|ltr")
706 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
707 QTest::newRow("<( )spacey> text |words|rtl")
708 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
709 QTest::newRow("spacey <text( )>|words|ltr")
710 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
712 // QTest::newRow("spacey text<( )>|words|rtl")
713 // << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
714 QTest::newRow("<()> spacey text |words")
715 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
716 QTest::newRow(" spacey text <()>|words")
717 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
720 void tst_qquicktextinput::moveCursorSelection()
722 QFETCH(QString, testStr);
723 QFETCH(int, cursorPosition);
724 QFETCH(int, movePosition);
725 QFETCH(QQuickTextInput::SelectionMode, mode);
726 QFETCH(int, selectionStart);
727 QFETCH(int, selectionEnd);
728 QFETCH(bool, reversible);
730 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
731 QDeclarativeComponent textinputComponent(&engine);
732 textinputComponent.setData(componentStr.toLatin1(), QUrl());
733 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
734 QVERIFY(textinputObject != 0);
736 textinputObject->setCursorPosition(cursorPosition);
737 textinputObject->moveCursorSelection(movePosition, mode);
739 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
740 QCOMPARE(textinputObject->selectionStart(), selectionStart);
741 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
744 textinputObject->setCursorPosition(movePosition);
745 textinputObject->moveCursorSelection(cursorPosition, mode);
747 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
748 QCOMPARE(textinputObject->selectionStart(), selectionStart);
749 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
752 delete textinputObject;
755 void tst_qquicktextinput::moveCursorSelectionSequence_data()
757 QTest::addColumn<QString>("testStr");
758 QTest::addColumn<int>("cursorPosition");
759 QTest::addColumn<int>("movePosition1");
760 QTest::addColumn<int>("movePosition2");
761 QTest::addColumn<int>("selection1Start");
762 QTest::addColumn<int>("selection1End");
763 QTest::addColumn<int>("selection2Start");
764 QTest::addColumn<int>("selection2End");
766 // () contains the text selected by the cursor.
767 // <> contains the actual selection.
768 // ^ is the revised cursor position.
769 // {} contains the revised selection.
771 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
776 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
781 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
786 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
791 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
796 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
801 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
806 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
811 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
816 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
821 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
826 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
831 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
836 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
841 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
846 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
851 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
856 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
862 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
867 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
873 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
878 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
884 QTest::newRow("{<(^} sp)acey> text |ltr")
889 QTest::newRow("{<( ^}sp)acey> text |ltr")
894 QTest::newRow("<( {s^p)acey>} text |rtl")
899 QTest::newRow("<( {^sp)acey>} text |rtl")
905 QTest::newRow(" spacey <te(xt {^)>}|rtl")
911 // QTest::newRow(" spacey <te(xt{^ )>}|rtl")
916 QTest::newRow(" spacey {<te(x^t} )>|ltr")
922 // QTest::newRow(" spacey {<te(xt^} )>|ltr")
929 void tst_qquicktextinput::moveCursorSelectionSequence()
931 QFETCH(QString, testStr);
932 QFETCH(int, cursorPosition);
933 QFETCH(int, movePosition1);
934 QFETCH(int, movePosition2);
935 QFETCH(int, selection1Start);
936 QFETCH(int, selection1End);
937 QFETCH(int, selection2Start);
938 QFETCH(int, selection2End);
940 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
941 QDeclarativeComponent textinputComponent(&engine);
942 textinputComponent.setData(componentStr.toLatin1(), QUrl());
943 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
944 QVERIFY(textinputObject != 0);
946 textinputObject->setCursorPosition(cursorPosition);
948 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
949 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
950 QCOMPARE(textinputObject->selectionStart(), selection1Start);
951 QCOMPARE(textinputObject->selectionEnd(), selection1End);
953 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
954 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
955 QCOMPARE(textinputObject->selectionStart(), selection2Start);
956 QCOMPARE(textinputObject->selectionEnd(), selection2End);
958 delete textinputObject;
961 void tst_qquicktextinput::dragMouseSelection()
963 QString qmlfile = TESTDATA("mouseselection_true.qml");
965 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
968 canvas.requestActivateWindow();
969 QTest::qWaitForWindowShown(&canvas);
971 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
973 QVERIFY(canvas.rootObject() != 0);
974 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
975 QVERIFY(textInputObject != 0);
977 // press-and-drag-and-release from x1 to x2
980 int y = textInputObject->height()/2;
981 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
982 QTest::mouseMove(&canvas, QPoint(x2, y));
983 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
986 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
987 QVERIFY(str1.length() > 3);
989 // press and drag the current selection.
992 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
993 QTest::mouseMove(&canvas, QPoint(x2, y));
994 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
996 QString str2 = textInputObject->selectedText();
997 QVERIFY(str2.length() > 3);
999 QVERIFY(str1 != str2);
1002 void tst_qquicktextinput::mouseSelectionMode_data()
1004 QTest::addColumn<QString>("qmlfile");
1005 QTest::addColumn<bool>("selectWords");
1008 QTest::newRow("SelectWords") << TESTDATA("mouseselectionmode_words.qml") << true;
1009 QTest::newRow("SelectCharacters") << TESTDATA("mouseselectionmode_characters.qml") << false;
1010 QTest::newRow("default") << TESTDATA("mouseselectionmode_default.qml") << false;
1013 void tst_qquicktextinput::mouseSelectionMode()
1015 QFETCH(QString, qmlfile);
1016 QFETCH(bool, selectWords);
1018 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1020 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1023 canvas.requestActivateWindow();
1024 QTest::qWaitForWindowShown(&canvas);
1025 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1027 QVERIFY(canvas.rootObject() != 0);
1028 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(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, QPoint(x2,y)); // doesn't work
1037 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1040 QTRY_COMPARE(textInputObject->selectedText(), text);
1042 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1043 QVERIFY(textInputObject->selectedText() != text);
1047 void tst_qquicktextinput::horizontalAlignment_data()
1049 QTest::addColumn<int>("hAlign");
1050 QTest::addColumn<QString>("expectfile");
1052 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1053 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1054 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1057 void tst_qquicktextinput::horizontalAlignment()
1059 QSKIP("Image comparison of text is almost guaranteed to fail during development");
1061 QFETCH(int, hAlign);
1062 QFETCH(QString, expectfile);
1064 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("horizontalAlignment.qml")));
1067 canvas.requestActivateWindow();
1068 QTest::qWaitForWindowShown(&canvas);
1069 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
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_qquicktextinput::horizontalAlignment_RightToLeft()
1084 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("horizontalAlignment_RightToLeft.qml")));
1085 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1086 QVERIFY(textInput != 0);
1089 const QString rtlText = textInput->text();
1091 QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::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(), QQuickTextInput::AlignRight);
1097 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1098 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1100 // explicitly left aligned
1101 textInput->setHAlign(QQuickTextInput::AlignLeft);
1102 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1103 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1104 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1106 // explicitly right aligned
1107 textInput->setHAlign(QQuickTextInput::AlignRight);
1108 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1109 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1110 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1112 // explicitly center aligned
1113 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1114 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1115 QCOMPARE(textInput->hAlign(), QQuickTextInput::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(), QQuickTextInput::AlignRight);
1122 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1123 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1125 // mirror the text item
1126 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1128 // mirrored implicit alignment should continue to follow the reading direction of the text
1129 QCOMPARE(textInput->hAlign(), QQuickTextInput::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(QQuickTextInput::AlignRight);
1135 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1136 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1137 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1139 // mirrored explicitly left aligned behaves as right aligned
1140 textInput->setHAlign(QQuickTextInput::AlignLeft);
1141 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1142 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1143 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1145 // disable mirroring
1146 QQuickItemPrivate::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(), QQuickTextInput::AlignLeft);
1153 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1155 canvas.requestActivateWindow();
1156 QTest::qWaitForWindowShown(&canvas);
1157 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
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>()); QGuiApplication::sendEvent(&canvas, &ev); }
1162 QEXPECT_FAIL("", "QTBUG-21691", Continue);
1163 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1164 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(&canvas, &ev); }
1165 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1168 // empty text with implicit alignment follows the system locale-based
1169 // keyboard input direction from QGuiApplication::keyboardInputDirection
1170 QEXPECT_FAIL("", "QTBUG-18040", Abort);
1172 textInput->setText("");
1173 QCOMPARE(textInput->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1174 QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1175 if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight)
1176 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1178 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1179 textInput->setHAlign(QQuickTextInput::AlignRight);
1180 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1181 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1185 QEXPECT_FAIL("", "QTBUG-18040", Abort); // alignment of TextInput with no text set to it
1187 QString componentStr = "import QtQuick 2.0\nTextInput {}";
1188 QDeclarativeComponent textComponent(&engine);
1189 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1190 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
1191 QCOMPARE(textObject->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1192 QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1196 void tst_qquicktextinput::positionAt()
1198 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
1199 QVERIFY(canvas.rootObject() != 0);
1201 canvas.requestActivateWindow();
1202 QTest::qWaitForWindowShown(&canvas);
1204 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1205 QVERIFY(textinputObject != 0);
1207 // Check autoscrolled...
1208 QFontMetrics fm(textinputObject->font());
1210 int pos = textinputObject->positionAt(textinputObject->width()/2);
1212 int textLeftWidth = 0;
1213 if (!qmlDisableDistanceField()) {
1215 QTextLayout layout(textinputObject->text().left(pos));
1219 option.setUseDesignMetrics(true);
1220 layout.setTextOption(option);
1223 layout.beginLayout();
1224 QTextLine line = layout.createLine();
1227 textLeftWidth = ceil(line.horizontalAdvance());
1230 QTextLayout layout(textinputObject->text());
1234 option.setUseDesignMetrics(true);
1235 layout.setTextOption(option);
1238 layout.beginLayout();
1239 QTextLine line = layout.createLine();
1242 textWidth = ceil(line.horizontalAdvance());
1245 textWidth = fm.width(textinputObject->text());
1246 textLeftWidth = fm.width(textinputObject->text().left(pos));
1249 int diff = abs(textWidth - (textLeftWidth+textinputObject->width()/2));
1251 // some tollerance for different fonts.
1252 QEXPECT_FAIL("", "QTBUG-21689", Abort);
1259 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1260 QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
1261 QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::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));
1289 // some tollerance for different fonts.
1296 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1297 QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
1298 QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorOnCharacter), pos);
1300 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1301 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1303 QString preeditText = textinputObject->text().mid(0, pos);
1304 textinputObject->setText(textinputObject->text().mid(pos));
1305 textinputObject->setCursorPosition(0);
1307 QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1308 QGuiApplication::sendEvent(&canvas, &inputEvent);
1310 // Check all points within the preedit text return the same position.
1311 QCOMPARE(textinputObject->positionAt(0), 0);
1312 QCOMPARE(textinputObject->positionAt(x0 / 2), 0);
1313 QCOMPARE(textinputObject->positionAt(x0), 0);
1315 // Verify positioning returns to normal after the preedit text.
1316 QCOMPARE(textinputObject->positionAt(x1), 1);
1317 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1320 void tst_qquicktextinput::maxLength()
1322 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("maxLength.qml")));
1323 QVERIFY(canvas.rootObject() != 0);
1325 canvas.requestActivateWindow();
1326 QTest::qWaitForWindowShown(&canvas);
1328 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1329 QVERIFY(textinputObject != 0);
1330 QVERIFY(textinputObject->text().isEmpty());
1331 QVERIFY(textinputObject->maxLength() == 10);
1332 foreach (const QString &str, standard) {
1333 QVERIFY(textinputObject->text().length() <= 10);
1334 textinputObject->setText(str);
1335 QVERIFY(textinputObject->text().length() <= 10);
1338 textinputObject->setText("");
1339 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1340 for (int i=0; i<20; i++) {
1341 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1342 //simulateKey(&canvas, Qt::Key_A);
1343 QTest::keyPress(&canvas, Qt::Key_A);
1344 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1349 void tst_qquicktextinput::masks()
1351 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1352 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1353 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("masks.qml")));
1355 canvas.requestActivateWindow();
1356 QVERIFY(canvas.rootObject() != 0);
1357 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1358 QVERIFY(textinputObject != 0);
1359 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1360 QVERIFY(textinputObject->text().length() == 0);
1361 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1362 for (int i=0; i<10; i++) {
1363 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1364 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1365 //simulateKey(&canvas, Qt::Key_A);
1366 QTest::keyPress(&canvas, Qt::Key_A);
1367 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1372 void tst_qquicktextinput::validators()
1374 // Note that this test assumes that the validators are working properly
1375 // so you may need to run their tests first. All validators are checked
1376 // here to ensure that their exposure to QML is working.
1378 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("validators.qml")));
1380 canvas.requestActivateWindow();
1382 QVERIFY(canvas.rootObject() != 0);
1384 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1386 intInput->setFocus(true);
1387 QTRY_VERIFY(intInput->hasActiveFocus());
1388 QTest::keyPress(&canvas, Qt::Key_1);
1389 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1391 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1392 QCOMPARE(intInput->hasAcceptableInput(), false);
1393 QTest::keyPress(&canvas, Qt::Key_2);
1394 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1396 QTRY_COMPARE(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);
1401 QCOMPARE(intInput->text(), QLatin1String("11"));
1402 QCOMPARE(intInput->hasAcceptableInput(), true);
1403 QTest::keyPress(&canvas, Qt::Key_0);
1404 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1406 QCOMPARE(intInput->text(), QLatin1String("11"));
1407 QCOMPARE(intInput->hasAcceptableInput(), true);
1409 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1410 QTRY_VERIFY(dblInput);
1411 dblInput->setFocus(true);
1412 QVERIFY(dblInput->hasActiveFocus() == true);
1413 QTest::keyPress(&canvas, Qt::Key_1);
1414 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1416 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1417 QCOMPARE(dblInput->hasAcceptableInput(), false);
1418 QTest::keyPress(&canvas, Qt::Key_2);
1419 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1421 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1422 QCOMPARE(dblInput->hasAcceptableInput(), true);
1423 QTest::keyPress(&canvas, Qt::Key_Period);
1424 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1426 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1427 QCOMPARE(dblInput->hasAcceptableInput(), true);
1428 QTest::keyPress(&canvas, Qt::Key_1);
1429 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1431 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1432 QCOMPARE(dblInput->hasAcceptableInput(), true);
1433 QTest::keyPress(&canvas, Qt::Key_1);
1434 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1436 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1437 QCOMPARE(dblInput->hasAcceptableInput(), true);
1438 QTest::keyPress(&canvas, Qt::Key_1);
1439 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1441 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1442 QCOMPARE(dblInput->hasAcceptableInput(), true);
1444 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1445 QTRY_VERIFY(strInput);
1446 strInput->setFocus(true);
1447 QVERIFY(strInput->hasActiveFocus() == true);
1448 QTest::keyPress(&canvas, Qt::Key_1);
1449 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1451 QTRY_COMPARE(strInput->text(), QLatin1String(""));
1452 QCOMPARE(strInput->hasAcceptableInput(), false);
1453 QTest::keyPress(&canvas, Qt::Key_A);
1454 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1456 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1457 QCOMPARE(strInput->hasAcceptableInput(), false);
1458 QTest::keyPress(&canvas, Qt::Key_A);
1459 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1461 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1462 QCOMPARE(strInput->hasAcceptableInput(), true);
1463 QTest::keyPress(&canvas, Qt::Key_A);
1464 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1466 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1467 QCOMPARE(strInput->hasAcceptableInput(), true);
1468 QTest::keyPress(&canvas, Qt::Key_A);
1469 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1471 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1472 QCOMPARE(strInput->hasAcceptableInput(), true);
1473 QTest::keyPress(&canvas, Qt::Key_A);
1474 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1476 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1477 QCOMPARE(strInput->hasAcceptableInput(), true);
1480 void tst_qquicktextinput::inputMethods()
1482 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("inputmethods.qml")));
1484 canvas.requestActivateWindow();
1485 QTest::qWaitForWindowShown(&canvas);
1487 // test input method hints
1488 QVERIFY(canvas.rootObject() != 0);
1489 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1490 QVERIFY(input != 0);
1491 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1492 input->setInputMethodHints(Qt::ImhUppercaseOnly);
1493 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1495 input->setFocus(true);
1496 QVERIFY(input->hasActiveFocus() == true);
1497 // test that input method event is committed
1498 QInputMethodEvent event;
1499 event.setCommitString( "My ", -12, 0);
1500 QGuiApplication::sendEvent(&canvas, &event);
1501 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
1502 QCOMPARE(input->text(), QString("My Hello world!"));
1504 input->setCursorPosition(2);
1505 event.setCommitString("Your", -2, 2);
1506 QGuiApplication::sendEvent(&canvas, &event);
1507 QCOMPARE(input->text(), QString("Your Hello world!"));
1508 QCOMPARE(input->cursorPosition(), 4);
1510 input->setCursorPosition(7);
1511 event.setCommitString("Goodbye", -2, 5);
1512 QGuiApplication::sendEvent(&canvas, &event);
1513 QCOMPARE(input->text(), QString("Your Goodbye world!"));
1514 QCOMPARE(input->cursorPosition(), 12);
1516 input->setCursorPosition(8);
1517 event.setCommitString("Our", -8, 4);
1518 QGuiApplication::sendEvent(&canvas, &event);
1519 QCOMPARE(input->text(), QString("Our Goodbye world!"));
1520 QCOMPARE(input->cursorPosition(), 7);
1524 TextInput element should only handle left/right keys until the cursor reaches
1525 the extent of the text, then they should ignore the keys.
1528 void tst_qquicktextinput::navigation()
1530 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1532 canvas.requestActivateWindow();
1534 QVERIFY(canvas.rootObject() != 0);
1536 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1538 QVERIFY(input != 0);
1539 input->setCursorPosition(0);
1540 QTRY_VERIFY(input->hasActiveFocus() == true);
1541 simulateKey(&canvas, Qt::Key_Left);
1542 QVERIFY(input->hasActiveFocus() == false);
1543 simulateKey(&canvas, Qt::Key_Right);
1544 QVERIFY(input->hasActiveFocus() == true);
1545 //QT-2944: If text is selected, ensure we deselect upon cursor motion
1546 input->setCursorPosition(input->text().length());
1547 input->select(0,input->text().length());
1548 QVERIFY(input->selectionStart() != input->selectionEnd());
1549 simulateKey(&canvas, Qt::Key_Right);
1550 QVERIFY(input->selectionStart() == input->selectionEnd());
1551 QVERIFY(input->selectionStart() == input->text().length());
1552 QVERIFY(input->hasActiveFocus() == true);
1553 simulateKey(&canvas, Qt::Key_Right);
1554 QVERIFY(input->hasActiveFocus() == false);
1555 simulateKey(&canvas, Qt::Key_Left);
1556 QVERIFY(input->hasActiveFocus() == true);
1558 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1559 input->setCursorPosition(2);
1560 QCOMPARE(input->cursorPosition(),2);
1561 simulateKey(&canvas, Qt::Key_Up);
1562 QCOMPARE(input->cursorPosition(),2);
1563 simulateKey(&canvas, Qt::Key_Down);
1564 QCOMPARE(input->cursorPosition(),2);
1567 void tst_qquicktextinput::navigation_RTL()
1569 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1571 canvas.requestActivateWindow();
1573 QVERIFY(canvas.rootObject() != 0);
1575 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1577 QVERIFY(input != 0);
1578 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1579 input->setText(QString::fromUtf16(arabic_str, 11));
1581 input->setCursorPosition(0);
1582 QTRY_VERIFY(input->hasActiveFocus() == true);
1585 simulateKey(&canvas, Qt::Key_Right);
1586 QVERIFY(input->hasActiveFocus() == false);
1589 simulateKey(&canvas, Qt::Key_Left);
1590 QVERIFY(input->hasActiveFocus() == true);
1592 input->setCursorPosition(input->text().length());
1593 QVERIFY(input->hasActiveFocus() == true);
1596 simulateKey(&canvas, Qt::Key_Left);
1597 QVERIFY(input->hasActiveFocus() == false);
1600 simulateKey(&canvas, Qt::Key_Right);
1601 QVERIFY(input->hasActiveFocus() == true);
1604 void tst_qquicktextinput::copyAndPaste() {
1605 #ifndef QT_NO_CLIPBOARD
1609 PasteboardRef pasteboard;
1610 OSStatus status = PasteboardCreate(0, &pasteboard);
1611 if (status == noErr)
1612 CFRelease(pasteboard);
1614 QSKIP("This machine doesn't support the clipboard");
1618 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1619 QDeclarativeComponent textInputComponent(&engine);
1620 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1621 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1622 QVERIFY(textInput != 0);
1625 QCOMPARE(textInput->text().length(), 12);
1626 textInput->select(0, textInput->text().length());;
1628 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1629 QCOMPARE(textInput->selectedText().length(), 12);
1630 textInput->setCursorPosition(0);
1631 QVERIFY(textInput->canPaste());
1633 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1634 QCOMPARE(textInput->text().length(), 24);
1637 QVERIFY(textInput->canPaste());
1638 textInput->setReadOnly(true);
1639 QVERIFY(!textInput->canPaste());
1640 textInput->setReadOnly(false);
1641 QVERIFY(textInput->canPaste());
1644 textInput->setCursorPosition(0);
1645 textInput->selectWord();
1646 QCOMPARE(textInput->selectedText(), QString("Hello"));
1648 // select all and cut
1649 textInput->selectAll();
1651 QCOMPARE(textInput->text().length(), 0);
1653 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1654 QCOMPARE(textInput->text().length(), 24);
1656 // clear copy buffer
1657 QClipboard *clipboard = QGuiApplication::clipboard();
1660 QVERIFY(!textInput->canPaste());
1662 // test that copy functionality is disabled
1663 // when echo mode is set to hide text/password mode
1666 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
1667 textInput->setEchoMode(echoMode);
1668 textInput->setText("My password");
1669 textInput->select(0, textInput->text().length());;
1671 if (echoMode == QQuickTextInput::Normal) {
1672 QVERIFY(!clipboard->text().isEmpty());
1673 QCOMPARE(clipboard->text(), QString("My password"));
1676 QVERIFY(clipboard->text().isEmpty());
1685 void tst_qquicktextinput::canPasteEmpty() {
1686 #ifndef QT_NO_CLIPBOARD
1688 QGuiApplication::clipboard()->clear();
1690 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1691 QDeclarativeComponent textInputComponent(&engine);
1692 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1693 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1694 QVERIFY(textInput != 0);
1697 bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1698 QCOMPARE(textInput->canPaste(), cp);
1703 void tst_qquicktextinput::canPaste() {
1704 #ifndef QT_NO_CLIPBOARD
1706 QGuiApplication::clipboard()->setText("Some text");
1708 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1709 QDeclarativeComponent textInputComponent(&engine);
1710 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1711 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1712 QVERIFY(textInput != 0);
1715 bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1716 QCOMPARE(textInput->canPaste(), cp);
1721 void tst_qquicktextinput::passwordCharacter()
1723 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
1724 QDeclarativeComponent textInputComponent(&engine);
1725 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1726 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1727 QVERIFY(textInput != 0);
1729 textInput->setPasswordCharacter("X");
1730 qreal implicitWidth = textInput->implicitWidth();
1731 textInput->setPasswordCharacter(".");
1733 // QTBUG-12383 content is updated and redrawn
1734 QVERIFY(textInput->implicitWidth() < implicitWidth);
1739 void tst_qquicktextinput::cursorDelegate()
1741 QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorTest.qml")));
1743 view.requestActivateWindow();
1744 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
1745 QVERIFY(textInputObject != 0);
1746 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
1747 //Test Delegate gets created
1748 textInputObject->setFocus(true);
1749 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
1750 QVERIFY(delegateObject);
1751 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1752 //Test Delegate gets moved
1753 for (int i=0; i<= textInputObject->text().length(); i++) {
1754 textInputObject->setCursorPosition(i);
1755 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1756 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1758 textInputObject->setCursorPosition(0);
1759 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1760 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1761 //Test Delegate gets deleted
1762 textInputObject->setCursorDelegate(0);
1763 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
1766 void tst_qquicktextinput::cursorVisible()
1768 QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorVisible.qml")));
1770 view.requestActivateWindow();
1771 QTest::qWaitForWindowShown(&view);
1772 QTRY_COMPARE(&view, qGuiApp->focusWindow());
1774 QQuickTextInput input;
1775 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
1777 QCOMPARE(input.isCursorVisible(), false);
1779 input.setCursorVisible(true);
1780 QCOMPARE(input.isCursorVisible(), true);
1781 QCOMPARE(spy.count(), 1);
1783 input.setCursorVisible(false);
1784 QCOMPARE(input.isCursorVisible(), false);
1785 QCOMPARE(spy.count(), 2);
1787 input.setFocus(true);
1788 QCOMPARE(input.isCursorVisible(), false);
1789 QCOMPARE(spy.count(), 2);
1791 input.setParentItem(view.rootObject());
1792 QCOMPARE(input.isCursorVisible(), true);
1793 QCOMPARE(spy.count(), 3);
1795 input.setFocus(false);
1796 QCOMPARE(input.isCursorVisible(), false);
1797 QCOMPARE(spy.count(), 4);
1799 input.setFocus(true);
1800 QCOMPARE(input.isCursorVisible(), true);
1801 QCOMPARE(spy.count(), 5);
1803 QQuickView alternateView;
1804 alternateView.show();
1805 alternateView.requestActivateWindow();
1806 QTest::qWaitForWindowShown(&alternateView);
1808 QCOMPARE(input.isCursorVisible(), false);
1809 QCOMPARE(spy.count(), 6);
1811 view.requestActivateWindow();
1812 QTest::qWaitForWindowShown(&view);
1813 QCOMPARE(input.isCursorVisible(), true);
1814 QCOMPARE(spy.count(), 7);
1817 void tst_qquicktextinput::cursorRectangle()
1819 QSKIP("QTBUG-21689");
1821 QString text = "Hello World!";
1823 QQuickTextInput input;
1824 input.setText(text);
1825 QFontMetricsF fm(input.font());
1826 input.setWidth(fm.width(text.mid(0, 5)));
1830 // some tolerance for different fonts.
1832 const int error = 2;
1834 const int error = 5;
1838 for (int i = 0; i <= 5; ++i) {
1839 input.setCursorPosition(i);
1840 r = input.cursorRectangle();
1841 int textWidth = fm.width(text.mid(0, i));
1843 QVERIFY(r.left() < textWidth + error);
1844 QVERIFY(r.right() > textWidth - error);
1845 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1848 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
1849 QVERIFY(r.left() < input.boundingRect().width());
1850 QVERIFY(r.right() >= input.width() - error);
1852 for (int i = 6; i < text.length(); ++i) {
1853 input.setCursorPosition(i);
1854 QCOMPARE(r, input.cursorRectangle());
1855 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1858 for (int i = text.length() - 2; i >= 0; --i) {
1859 input.setCursorPosition(i);
1860 r = input.cursorRectangle();
1861 QVERIFY(r.right() >= 0);
1862 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1865 input.setText("Hi!");
1866 input.setHAlign(QQuickTextInput::AlignRight);
1867 r = input.cursorRectangle();
1868 QVERIFY(r.left() < input.boundingRect().width());
1869 QVERIFY(r.right() >= input.width() - error);
1872 void tst_qquicktextinput::readOnly()
1874 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("readOnly.qml")));
1876 canvas.requestActivateWindow();
1878 QVERIFY(canvas.rootObject() != 0);
1880 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1882 QVERIFY(input != 0);
1883 QTRY_VERIFY(input->hasActiveFocus() == true);
1884 QVERIFY(input->isReadOnly() == true);
1885 QString initial = input->text();
1886 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
1887 simulateKey(&canvas, k);
1888 simulateKey(&canvas, Qt::Key_Return);
1889 simulateKey(&canvas, Qt::Key_Space);
1890 simulateKey(&canvas, Qt::Key_Escape);
1891 QCOMPARE(input->text(), initial);
1893 input->setCursorPosition(3);
1894 input->setReadOnly(false);
1895 QCOMPARE(input->isReadOnly(), false);
1896 QCOMPARE(input->cursorPosition(), input->text().length());
1899 void tst_qquicktextinput::echoMode()
1901 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("echoMode.qml")));
1903 canvas.requestActivateWindow();
1904 QTest::qWaitForWindowShown(&canvas);
1905 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1907 QVERIFY(canvas.rootObject() != 0);
1909 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1911 QVERIFY(input != 0);
1912 QTRY_VERIFY(input->hasActiveFocus() == true);
1913 QString initial = input->text();
1914 Qt::InputMethodHints ref;
1915 QCOMPARE(initial, QLatin1String("ABCDefgh"));
1916 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
1917 QCOMPARE(input->displayText(), input->text());
1919 ref &= ~Qt::ImhHiddenText;
1920 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1921 QCOMPARE(input->inputMethodHints(), ref);
1922 input->setEchoMode(QQuickTextInput::NoEcho);
1923 QCOMPARE(input->text(), initial);
1924 QCOMPARE(input->displayText(), QLatin1String(""));
1925 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
1927 ref |= Qt::ImhHiddenText;
1928 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1929 QCOMPARE(input->inputMethodHints(), ref);
1930 input->setEchoMode(QQuickTextInput::Password);
1932 ref |= Qt::ImhHiddenText;
1933 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1934 QCOMPARE(input->text(), initial);
1935 QCOMPARE(input->displayText(), QLatin1String("********"));
1936 QCOMPARE(input->inputMethodHints(), ref);
1937 input->setPasswordCharacter(QChar('Q'));
1938 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
1939 QCOMPARE(input->text(), initial);
1940 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1941 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
1942 //PasswordEchoOnEdit
1943 ref &= ~Qt::ImhHiddenText;
1944 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1945 QCOMPARE(input->inputMethodHints(), ref);
1946 QCOMPARE(input->text(), initial);
1947 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1948 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
1949 QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
1950 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1951 QCOMPARE(input->text(), QLatin1String("a"));
1952 QCOMPARE(input->displayText(), QLatin1String("a"));
1953 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
1954 input->setFocus(false);
1955 QVERIFY(input->hasActiveFocus() == false);
1956 QCOMPARE(input->displayText(), QLatin1String("Q"));
1957 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
1958 input->setFocus(true);
1959 QVERIFY(input->hasActiveFocus());
1960 QInputMethodEvent inputEvent;
1961 inputEvent.setCommitString(initial);
1962 QGuiApplication::sendEvent(input, &inputEvent);
1963 QCOMPARE(input->text(), initial);
1964 QCOMPARE(input->displayText(), initial);
1965 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
1968 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
1969 void tst_qdeclarativetextinput::passwordEchoDelay()
1971 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("echoMode.qml")));
1974 QGuiApplication::setActiveWindow(&canvas);
1975 QTest::qWaitForWindowShown(&canvas);
1976 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1978 QVERIFY(canvas.rootObject() != 0);
1980 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1982 QChar fillChar = QLatin1Char('*');
1984 input->setEchoMode(QDeclarativeTextInput::Password);
1985 QCOMPARE(input->displayText(), QString(8, fillChar));
1986 input->setText(QString());
1987 QCOMPARE(input->displayText(), QString());
1989 QTest::keyPress(&canvas, '0');
1990 QTest::keyPress(&canvas, '1');
1991 QTest::keyPress(&canvas, '2');
1992 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
1993 QTest::keyPress(&canvas, '3');
1994 QTest::keyPress(&canvas, '4');
1995 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1996 QTest::keyPress(&canvas, Qt::Key_Backspace);
1997 QCOMPARE(input->displayText(), QString(4, fillChar));
1998 QTest::keyPress(&canvas, '4');
1999 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2000 QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
2001 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2002 QTest::keyPress(&canvas, '5');
2003 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2004 input->setFocus(false);
2005 QVERIFY(!input->hasFocus());
2006 QCOMPARE(input->displayText(), QString(6, fillChar));
2007 input->setFocus(true);
2008 QTRY_VERIFY(input->hasFocus());
2009 QCOMPARE(input->displayText(), QString(6, fillChar));
2010 QTest::keyPress(&canvas, '6');
2011 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2013 QInputMethodEvent ev;
2014 ev.setCommitString(QLatin1String("7"));
2015 QGuiApplication::sendEvent(&canvas, &ev);
2016 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2021 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2023 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2024 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2026 QGuiApplication::sendEvent(view, &press);
2027 QGuiApplication::sendEvent(view, &release);
2031 class MyInputContext : public QInputContext
2034 MyInputContext() : updateReceived(false), eventType(QEvent::None) {}
2035 ~MyInputContext() {}
2037 QString identifierName() { return QString(); }
2038 QString language() { return QString(); }
2042 bool isComposing() const { return false; }
2044 void update() { updateReceived = true; }
2046 void mouseHandler(int x, QMouseEvent *event)
2049 eventType = event->type();
2050 eventPosition = event->pos();
2051 eventGlobalPosition = event->globalPos();
2052 eventButton = event->button();
2053 eventButtons = event->buttons();
2054 eventModifiers = event->modifiers();
2057 void sendPreeditText(const QString &text, int cursor)
2059 QList<QInputMethodEvent::Attribute> attributes;
2060 attributes.append(QInputMethodEvent::Attribute(
2061 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2063 QInputMethodEvent event(text, attributes);
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;
2078 void tst_qquicktextinput::openInputPanel()
2080 QQuickView view(QUrl::fromLocalFile(TESTDATA("openInputPanel.qml")));
2082 view.requestActivateWindow();
2083 QTest::qWaitForWindowShown(&view);
2084 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2086 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2089 // check default values
2090 QVERIFY(input->focusOnPress());
2091 QVERIFY(!input->hasActiveFocus());
2092 qDebug() << &input << qApp->inputPanel()->inputItem();
2093 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2094 QEXPECT_FAIL("", "QTBUG-21946", Abort);
2095 QCOMPARE(qApp->inputPanel()->visible(), false);
2097 // input panel should open on focus
2098 QPoint centerPoint(view.width()/2, view.height()/2);
2099 Qt::KeyboardModifiers noModifiers = 0;
2100 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2101 QGuiApplication::processEvents();
2102 QVERIFY(input->hasActiveFocus());
2103 QCOMPARE(qApp->inputPanel()->inputItem(), input);
2104 QCOMPARE(qApp->inputPanel()->visible(), true);
2105 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2107 // input panel should be re-opened when pressing already focused TextInput
2108 qApp->inputPanel()->hide();
2109 QCOMPARE(qApp->inputPanel()->visible(), false);
2110 QVERIFY(input->hasActiveFocus());
2111 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2112 QGuiApplication::processEvents();
2113 QCOMPARE(qApp->inputPanel()->visible(), true);
2114 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2116 // input panel should stay visible if focus is lost to another text inputor
2117 QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2118 QQuickTextInput anotherInput;
2119 anotherInput.setParentItem(view.rootObject());
2120 anotherInput.setFocus(true);
2121 QCOMPARE(qApp->inputPanel()->visible(), true);
2122 QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2123 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2125 anotherInput.setFocus(false);
2126 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2127 QCOMPARE(view.activeFocusItem(), view.rootItem());
2128 anotherInput.setFocus(true);
2130 // input item should be null if focus is lost to an item that doesn't accept inputs
2132 item.setParentItem(view.rootObject());
2133 item.setFocus(true);
2134 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2135 QCOMPARE(view.activeFocusItem(), &item);
2137 qApp->inputPanel()->hide();
2139 // input panel should not be opened if TextInput is read only
2140 input->setReadOnly(true);
2141 input->setFocus(true);
2142 QCOMPARE(qApp->inputPanel()->visible(), false);
2143 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2144 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2145 QGuiApplication::processEvents();
2146 QCOMPARE(qApp->inputPanel()->visible(), false);
2148 // input panel should not be opened if focusOnPress is set to false
2149 input->setFocusOnPress(false);
2150 input->setFocus(false);
2151 input->setFocus(true);
2152 QCOMPARE(qApp->inputPanel()->visible(), false);
2153 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2154 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2155 QCOMPARE(qApp->inputPanel()->visible(), false);
2157 // input panel should open when openSoftwareInputPanel is called
2158 input->openSoftwareInputPanel();
2159 QCOMPARE(qApp->inputPanel()->visible(), true);
2161 // input panel should close when closeSoftwareInputPanel is called
2162 input->closeSoftwareInputPanel();
2163 QCOMPARE(qApp->inputPanel()->visible(), false);
2166 class MyTextInput : public QQuickTextInput
2169 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2173 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2176 return QQuickTextInput::updatePaintNode(node, data);
2181 void tst_qquicktextinput::setHAlignClearCache()
2185 input.setText("Hello world");
2186 input.setParentItem(view.rootItem());
2188 view.requestActivateWindow();
2189 QTest::qWaitForWindowShown(&view);
2190 QTRY_COMPARE(input.nbPaint, 1);
2191 input.setHAlign(QQuickTextInput::AlignRight);
2192 //Changing the alignment should trigger a repaint
2193 QTRY_COMPARE(input.nbPaint, 2);
2196 void tst_qquicktextinput::focusOutClearSelection()
2199 QQuickTextInput input;
2200 QQuickTextInput input2;
2201 input.setText(QLatin1String("Hello world"));
2202 input.setFocus(true);
2203 input2.setParentItem(view.rootItem());
2204 input.setParentItem(view.rootItem());
2206 view.requestActivateWindow();
2207 QTest::qWaitForWindowShown(&view);
2209 //The selection should work
2210 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2211 input2.setFocus(true);
2212 QGuiApplication::processEvents();
2213 //The input lost the focus selection should be cleared
2214 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2217 void tst_qquicktextinput::geometrySignals()
2219 QDeclarativeComponent component(&engine, TESTDATA("geometrySignals.qml"));
2220 QObject *o = component.create();
2222 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2223 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2227 void tst_qquicktextinput::testQtQuick11Attributes()
2229 QFETCH(QString, code);
2230 QFETCH(QString, warning);
2231 QFETCH(QString, error);
2233 QDeclarativeEngine engine;
2236 QDeclarativeComponent valid(&engine);
2237 valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2238 obj = valid.create();
2240 QVERIFY(valid.errorString().isEmpty());
2243 QDeclarativeComponent invalid(&engine);
2244 invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2245 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2246 obj = invalid.create();
2247 QCOMPARE(invalid.errorString(), error);
2251 void tst_qquicktextinput::testQtQuick11Attributes_data()
2253 QTest::addColumn<QString>("code");
2254 QTest::addColumn<QString>("warning");
2255 QTest::addColumn<QString>("error");
2257 QTest::newRow("canPaste") << "property bool foo: canPaste"
2258 << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2261 QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2262 << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2265 QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2266 << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2270 void tst_qquicktextinput::preeditAutoScroll()
2273 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2276 QString preeditText = "califragisiticexpialidocious!";
2278 QQuickView view(QUrl::fromLocalFile(TESTDATA("preeditAutoScroll.qml")));
2280 // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2281 // and QWidget won't allow an input context to be set when the flag is not set.
2282 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2283 view.setInputContext(&ic);
2284 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2286 view.requestActivateWindow();
2287 QTest::qWaitForWindowShown(&view);
2288 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2289 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2292 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2293 int cursorRectangleChanges = 0;
2295 QFontMetricsF fm(input->font());
2296 input->setWidth(fm.width(input->text()));
2298 // test the text is scrolled so the preedit is visible.
2299 ic.sendPreeditText(preeditText.mid(0, 3), 1);
2300 QVERIFY(input->positionAt(0) != 0);
2301 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2302 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2304 // test the text is scrolled back when the preedit is removed.
2305 ic.sendEvent(QInputMethodEvent());
2306 QCOMPARE(input->positionAt(0), 0);
2307 QCOMPARE(input->positionAt(input->width()), 5);
2308 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2310 // some tolerance for different fonts.
2312 const int error = 2;
2314 const int error = 5;
2317 // test if the preedit is larger than the text input that the
2318 // character preceding the cursor is still visible.
2319 qreal x = input->positionToRectangle(0).x();
2320 for (int i = 0; i < 3; ++i) {
2321 ic.sendPreeditText(preeditText, i + 1);
2322 QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2323 QVERIFY(input->positionToRectangle(0).x() < x);
2324 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2325 x = input->positionToRectangle(0).x();
2327 for (int i = 1; i >= 0; --i) {
2328 ic.sendPreeditText(preeditText, i + 1);
2329 QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2330 QVERIFY(input->positionToRectangle(0).x() > x);
2331 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2332 x = input->positionToRectangle(0).x();
2335 // Test incrementing the preedit cursor doesn't cause further
2336 // scrolling when right most text is visible.
2337 ic.sendPreeditText(preeditText, preeditText.length() - 3);
2338 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2339 x = input->positionToRectangle(0).x();
2340 for (int i = 2; i >= 0; --i) {
2341 ic.sendPreeditText(preeditText, preeditText.length() - i);
2342 QCOMPARE(input->positionToRectangle(0).x(), x);
2343 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2345 for (int i = 1; i < 3; ++i) {
2346 ic.sendPreeditText(preeditText, preeditText.length() - i);
2347 QCOMPARE(input->positionToRectangle(0).x(), x);
2348 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2351 // Test disabling auto scroll.
2352 ic.sendEvent(QInputMethodEvent());
2354 input->setAutoScroll(false);
2355 ic.sendPreeditText(preeditText.mid(0, 3), 1);
2356 QCOMPARE(input->positionAt(0), 0);
2357 QCOMPARE(input->positionAt(input->width()), 5);
2361 void tst_qquicktextinput::preeditMicroFocus()
2364 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2367 QString preeditText = "super";
2369 QQuickView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml")));
2371 // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2372 // and QWidget won't allow an input context to be set when the flag is not set.
2373 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2374 view.setInputContext(&ic);
2375 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2377 view.requestActivateWindow();
2378 QTest::qWaitForWindowShown(&view);
2379 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2380 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2384 QRect previousRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2386 // Verify that the micro focus rect is positioned the same for position 0 as
2387 // it would be if there was no preedit text.
2388 ic.updateReceived = false;
2389 ic.sendPreeditText(preeditText, 0);
2390 currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2391 QCOMPARE(currentRect, previousRect);
2392 QCOMPARE(ic.updateReceived, true);
2394 // Verify that the micro focus rect moves to the left as the cursor position
2396 for (int i = 1; i <= 5; ++i) {
2397 ic.updateReceived = false;
2398 ic.sendPreeditText(preeditText, i);
2399 currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2400 QVERIFY(previousRect.left() < currentRect.left());
2402 QCOMPARE(ic.updateReceived, true);
2403 previousRect = currentRect;
2406 // Verify that if there is no preedit cursor then the micro focus rect is the
2407 // same as it would be if it were positioned at the end of the preedit text.
2408 ic.sendPreeditText(preeditText, 0);
2409 ic.updateReceived = false;
2410 ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2411 currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2412 QCOMPARE(currentRect, previousRect);
2413 QCOMPARE(ic.updateReceived, true);
2417 void tst_qquicktextinput::inputContextMouseHandler()
2420 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2423 QString text = "supercalifragisiticexpialidocious!";
2425 QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2427 // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2428 // and QWidget won't allow an input context to be set when the flag is not set.
2429 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2430 view.setInputContext(&ic);
2431 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2433 view.requestActivateWindow();
2434 QTest::qWaitForWindowShown(&view);
2435 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2436 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2439 QFontMetricsF fm(input->font());
2440 const qreal y = fm.height() / 2;
2442 QPoint position2 = input->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
2443 QPoint position8 = input->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
2444 QPoint position20 = input->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
2445 QPoint position27 = input->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
2446 QPoint globalPosition2 = view.mapToGlobal(position2);
2447 QPoint globalposition8 = view.mapToGlobal(position8);
2448 QPoint globalposition20 = view.mapToGlobal(position20);
2449 QPoint globalposition27 = view.mapToGlobal(position27);
2451 ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2453 QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
2454 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2455 QCOMPARE(ic.eventPosition, position2);
2456 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2457 QCOMPARE(ic.eventButton, Qt::LeftButton);
2458 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2459 QVERIFY(ic.cursor < 0);
2460 ic.eventType = QEvent::None;
2462 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
2463 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2464 QCOMPARE(ic.eventPosition, position2);
2465 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2466 QCOMPARE(ic.eventButton, Qt::LeftButton);
2467 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2468 QVERIFY(ic.cursor < 0);
2469 ic.eventType = QEvent::None;
2471 { QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2472 QGuiApplication::sendEvent(&view, &mv); }
2473 QCOMPARE(ic.eventType, QEvent::None);
2475 { QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2476 QGuiApplication::sendEvent(&view, &mv); }
2477 QCOMPARE(ic.eventType, QEvent::MouseMove);
2478 QCOMPARE(ic.eventPosition, position27);
2479 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2480 QCOMPARE(ic.eventButton, Qt::LeftButton);
2481 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2482 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); // 15 is expected but some platforms may be off by one.
2483 ic.eventType = QEvent::None;
2485 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
2486 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2487 QCOMPARE(ic.eventPosition, position27);
2488 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2489 QCOMPARE(ic.eventButton, Qt::LeftButton);
2490 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2491 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2492 ic.eventType = QEvent::None;
2494 // And in the other direction.
2495 QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
2496 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2497 QCOMPARE(ic.eventPosition, position27);
2498 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2499 QCOMPARE(ic.eventButton, Qt::LeftButton);
2500 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2501 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2502 ic.eventType = QEvent::None;
2504 QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
2505 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2506 QCOMPARE(ic.eventPosition, position27);
2507 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2508 QCOMPARE(ic.eventButton, Qt::RightButton);
2509 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2510 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2511 ic.eventType = QEvent::None;
2513 { QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2514 QGuiApplication::sendEvent(&view, &mv); }
2515 QCOMPARE(ic.eventType, QEvent::MouseMove);
2516 QCOMPARE(ic.eventPosition, position20);
2517 QCOMPARE(ic.eventGlobalPosition, globalposition20);
2518 QCOMPARE(ic.eventButton, Qt::RightButton);
2519 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2520 QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2521 ic.eventType = QEvent::None;
2523 { QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2524 QGuiApplication::sendEvent(&view, &mv); }
2525 QCOMPARE(ic.eventType, QEvent::None);
2527 QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
2528 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2529 QCOMPARE(ic.eventPosition, position2);
2530 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2531 QCOMPARE(ic.eventButton, Qt::RightButton);
2532 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2533 QVERIFY(ic.cursor < 0);
2534 ic.eventType = QEvent::None;
2538 void tst_qquicktextinput::inputMethodComposing()
2540 QString text = "supercalifragisiticexpialidocious!";
2542 QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2544 view.requestActivateWindow();
2545 QTest::qWaitForWindowShown(&view);
2546 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2547 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2549 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2551 QCOMPARE(input->isInputMethodComposing(), false);
2553 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2554 QGuiApplication::sendEvent(input, &event);
2556 QCOMPARE(input->isInputMethodComposing(), true);
2557 QCOMPARE(spy.count(), 1);
2560 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2561 QGuiApplication::sendEvent(input, &event);
2563 QCOMPARE(spy.count(), 1);
2566 QInputMethodEvent event;
2567 QGuiApplication::sendEvent(input, &event);
2569 QCOMPARE(input->isInputMethodComposing(), false);
2570 QCOMPARE(spy.count(), 2);
2573 void tst_qquicktextinput::cursorRectangleSize()
2575 QQuickView *canvas = new QQuickView(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
2576 QVERIFY(canvas->rootObject() != 0);
2578 canvas->requestActivateWindow();
2579 QTest::qWaitForWindowShown(canvas);
2581 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
2582 QVERIFY(textInput != 0);
2583 textInput->setFocus(Qt::OtherFocusReason);
2584 QRectF cursorRect = textInput->positionToRectangle(textInput->cursorPosition());
2585 QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImCursorRectangle).toRectF();
2586 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2587 qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
2589 QRectF microFocusFromApp = event.value(Qt::ImCursorRectangle).toRectF();
2591 QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2592 QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2597 void tst_qquicktextinput::tripleClickSelectsAll()
2599 QString qmlfile = TESTDATA("positionAt.qml");
2600 QQuickView view(QUrl::fromLocalFile(qmlfile));
2602 view.requestActivateWindow();
2603 QTest::qWaitForWindowShown(&view);
2605 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2607 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
2610 QLatin1String hello("Hello world!");
2611 input->setSelectByMouse(true);
2612 input->setText(hello);
2614 // Clicking on the same point inside TextInput three times in a row
2615 // should trigger a triple click, thus selecting all the text.
2616 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
2617 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2618 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2619 QGuiApplication::processEvents();
2620 QCOMPARE(input->selectedText(), hello);
2622 // Now it simulates user moving the mouse between the second and the third click.
2623 // In this situation, we don't expect a triple click.
2624 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
2625 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2626 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
2627 QGuiApplication::processEvents();
2628 QVERIFY(input->selectedText().isEmpty());
2630 // And now we press the third click too late, so no triple click event is triggered.
2631 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2632 QGuiApplication::processEvents();
2633 QTest::qWait(QApplication::doubleClickInterval() + 1);
2634 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2635 QGuiApplication::processEvents();
2636 QVERIFY(input->selectedText().isEmpty());
2639 void tst_qquicktextinput::QTBUG_19956_data()
2641 QTest::addColumn<QString>("url");
2642 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
2643 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
2646 void tst_qquicktextinput::QTBUG_19956()
2648 QFETCH(QString, url);
2650 QQuickView canvas(QUrl::fromLocalFile(TESTDATA(url)));
2652 canvas.requestActivateWindow();
2653 QTest::qWaitForWindowShown(&canvas);
2654 QVERIFY(canvas.rootObject() != 0);
2655 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
2657 input->setFocus(true);
2658 QVERIFY(input->hasActiveFocus());
2660 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
2661 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
2662 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
2663 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2665 canvas.rootObject()->setProperty("topvalue", 15);
2666 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
2667 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2669 canvas.rootObject()->setProperty("topvalue", 25);
2670 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
2671 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2673 canvas.rootObject()->setProperty("bottomvalue", 21);
2674 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
2675 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2677 canvas.rootObject()->setProperty("bottomvalue", 10);
2678 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
2679 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2682 void tst_qquicktextinput::QTBUG_19956_regexp()
2684 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("qtbug-19956regexp.qml")));
2686 canvas.requestActivateWindow();
2687 QTest::qWaitForWindowShown(&canvas);
2688 QVERIFY(canvas.rootObject() != 0);
2689 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
2691 input->setFocus(true);
2692 QVERIFY(input->hasActiveFocus());
2694 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
2695 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
2696 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
2697 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2699 canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
2700 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
2701 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2703 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
2704 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
2705 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2708 QTEST_MAIN(tst_qquicktextinput)
2710 #include "tst_qquicktextinput.moc"