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);
1167 #ifndef Q_OS_MAC // QTBUG-18040
1168 // empty text with implicit alignment follows the system locale-based
1169 // keyboard input direction from QGuiApplication::keyboardInputDirection
1170 textInput->setText("");
1171 QCOMPARE(textInput->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1172 QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1173 if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight)
1174 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1176 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1177 textInput->setHAlign(QQuickTextInput::AlignRight);
1178 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1179 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1182 #ifndef Q_OS_MAC // QTBUG-18040
1183 // alignment of TextInput with no text set to it
1184 QString componentStr = "import QtQuick 2.0\nTextInput {}";
1185 QDeclarativeComponent textComponent(&engine);
1186 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1187 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
1188 QCOMPARE(textObject->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1189 QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1194 void tst_qquicktextinput::positionAt()
1196 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
1197 QVERIFY(canvas.rootObject() != 0);
1199 canvas.requestActivateWindow();
1200 QTest::qWaitForWindowShown(&canvas);
1202 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1203 QVERIFY(textinputObject != 0);
1205 // Check autoscrolled...
1206 QFontMetrics fm(textinputObject->font());
1208 int pos = textinputObject->positionAt(textinputObject->width()/2);
1210 int textLeftWidth = 0;
1211 if (!qmlDisableDistanceField()) {
1213 QTextLayout layout(textinputObject->text().left(pos));
1217 option.setUseDesignMetrics(true);
1218 layout.setTextOption(option);
1221 layout.beginLayout();
1222 QTextLine line = layout.createLine();
1225 textLeftWidth = ceil(line.horizontalAdvance());
1228 QTextLayout layout(textinputObject->text());
1232 option.setUseDesignMetrics(true);
1233 layout.setTextOption(option);
1236 layout.beginLayout();
1237 QTextLine line = layout.createLine();
1240 textWidth = ceil(line.horizontalAdvance());
1243 textWidth = fm.width(textinputObject->text());
1244 textLeftWidth = fm.width(textinputObject->text().left(pos));
1247 int diff = abs(textWidth - (textLeftWidth+textinputObject->width()/2));
1249 // some tollerance for different fonts.
1250 QEXPECT_FAIL("", "QTBUG-21689", Abort);
1257 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1258 QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
1259 QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorOnCharacter), pos);
1261 // Check without autoscroll...
1262 textinputObject->setAutoScroll(false);
1263 pos = textinputObject->positionAt(textinputObject->width()/2);
1265 if (!qmlDisableDistanceField()) {
1267 QTextLayout layout(textinputObject->text().left(pos));
1271 option.setUseDesignMetrics(true);
1272 layout.setTextOption(option);
1275 layout.beginLayout();
1276 QTextLine line = layout.createLine();
1279 textLeftWidth = ceil(line.horizontalAdvance());
1282 textLeftWidth = fm.width(textinputObject->text().left(pos));
1285 diff = abs(int(textLeftWidth-textinputObject->width()/2));
1287 // some tollerance for different fonts.
1294 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1295 QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
1296 QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorOnCharacter), pos);
1298 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1299 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1301 QString preeditText = textinputObject->text().mid(0, pos);
1302 textinputObject->setText(textinputObject->text().mid(pos));
1303 textinputObject->setCursorPosition(0);
1305 QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1306 QGuiApplication::sendEvent(&canvas, &inputEvent);
1308 // Check all points within the preedit text return the same position.
1309 QCOMPARE(textinputObject->positionAt(0), 0);
1310 QCOMPARE(textinputObject->positionAt(x0 / 2), 0);
1311 QCOMPARE(textinputObject->positionAt(x0), 0);
1313 // Verify positioning returns to normal after the preedit text.
1314 QCOMPARE(textinputObject->positionAt(x1), 1);
1315 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1318 void tst_qquicktextinput::maxLength()
1320 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("maxLength.qml")));
1321 QVERIFY(canvas.rootObject() != 0);
1323 canvas.requestActivateWindow();
1324 QTest::qWaitForWindowShown(&canvas);
1326 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1327 QVERIFY(textinputObject != 0);
1328 QVERIFY(textinputObject->text().isEmpty());
1329 QVERIFY(textinputObject->maxLength() == 10);
1330 foreach (const QString &str, standard) {
1331 QVERIFY(textinputObject->text().length() <= 10);
1332 textinputObject->setText(str);
1333 QVERIFY(textinputObject->text().length() <= 10);
1336 textinputObject->setText("");
1337 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1338 for (int i=0; i<20; i++) {
1339 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1340 //simulateKey(&canvas, Qt::Key_A);
1341 QTest::keyPress(&canvas, Qt::Key_A);
1342 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1347 void tst_qquicktextinput::masks()
1349 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1350 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1351 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("masks.qml")));
1353 canvas.requestActivateWindow();
1354 QVERIFY(canvas.rootObject() != 0);
1355 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1356 QVERIFY(textinputObject != 0);
1357 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1358 QVERIFY(textinputObject->text().length() == 0);
1359 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1360 for (int i=0; i<10; i++) {
1361 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1362 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1363 //simulateKey(&canvas, Qt::Key_A);
1364 QTest::keyPress(&canvas, Qt::Key_A);
1365 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1370 void tst_qquicktextinput::validators()
1372 // Note that this test assumes that the validators are working properly
1373 // so you may need to run their tests first. All validators are checked
1374 // here to ensure that their exposure to QML is working.
1376 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("validators.qml")));
1378 canvas.requestActivateWindow();
1380 QVERIFY(canvas.rootObject() != 0);
1382 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1384 intInput->setFocus(true);
1385 QTRY_VERIFY(intInput->hasActiveFocus());
1386 QTest::keyPress(&canvas, Qt::Key_1);
1387 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1389 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1390 QCOMPARE(intInput->hasAcceptableInput(), false);
1391 QTest::keyPress(&canvas, Qt::Key_2);
1392 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1394 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1395 QCOMPARE(intInput->hasAcceptableInput(), false);
1396 QTest::keyPress(&canvas, Qt::Key_1);
1397 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1399 QCOMPARE(intInput->text(), QLatin1String("11"));
1400 QCOMPARE(intInput->hasAcceptableInput(), true);
1401 QTest::keyPress(&canvas, Qt::Key_0);
1402 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1404 QCOMPARE(intInput->text(), QLatin1String("11"));
1405 QCOMPARE(intInput->hasAcceptableInput(), true);
1407 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1408 QTRY_VERIFY(dblInput);
1409 dblInput->setFocus(true);
1410 QVERIFY(dblInput->hasActiveFocus() == true);
1411 QTest::keyPress(&canvas, Qt::Key_1);
1412 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1414 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1415 QCOMPARE(dblInput->hasAcceptableInput(), false);
1416 QTest::keyPress(&canvas, Qt::Key_2);
1417 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1419 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1420 QCOMPARE(dblInput->hasAcceptableInput(), true);
1421 QTest::keyPress(&canvas, Qt::Key_Period);
1422 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1424 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1425 QCOMPARE(dblInput->hasAcceptableInput(), true);
1426 QTest::keyPress(&canvas, Qt::Key_1);
1427 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1429 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1430 QCOMPARE(dblInput->hasAcceptableInput(), true);
1431 QTest::keyPress(&canvas, Qt::Key_1);
1432 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1434 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1435 QCOMPARE(dblInput->hasAcceptableInput(), true);
1436 QTest::keyPress(&canvas, Qt::Key_1);
1437 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1439 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1440 QCOMPARE(dblInput->hasAcceptableInput(), true);
1442 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1443 QTRY_VERIFY(strInput);
1444 strInput->setFocus(true);
1445 QVERIFY(strInput->hasActiveFocus() == true);
1446 QTest::keyPress(&canvas, Qt::Key_1);
1447 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1449 QTRY_COMPARE(strInput->text(), QLatin1String(""));
1450 QCOMPARE(strInput->hasAcceptableInput(), false);
1451 QTest::keyPress(&canvas, Qt::Key_A);
1452 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1454 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1455 QCOMPARE(strInput->hasAcceptableInput(), false);
1456 QTest::keyPress(&canvas, Qt::Key_A);
1457 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1459 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1460 QCOMPARE(strInput->hasAcceptableInput(), true);
1461 QTest::keyPress(&canvas, Qt::Key_A);
1462 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1464 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1465 QCOMPARE(strInput->hasAcceptableInput(), true);
1466 QTest::keyPress(&canvas, Qt::Key_A);
1467 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1469 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1470 QCOMPARE(strInput->hasAcceptableInput(), true);
1471 QTest::keyPress(&canvas, Qt::Key_A);
1472 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1474 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1475 QCOMPARE(strInput->hasAcceptableInput(), true);
1478 void tst_qquicktextinput::inputMethods()
1480 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("inputmethods.qml")));
1482 canvas.requestActivateWindow();
1483 QTest::qWaitForWindowShown(&canvas);
1485 // test input method hints
1486 QVERIFY(canvas.rootObject() != 0);
1487 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1488 QVERIFY(input != 0);
1489 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1490 input->setInputMethodHints(Qt::ImhUppercaseOnly);
1491 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1493 input->setFocus(true);
1494 QVERIFY(input->hasActiveFocus() == true);
1495 // test that input method event is committed
1496 QInputMethodEvent event;
1497 event.setCommitString( "My ", -12, 0);
1498 QGuiApplication::sendEvent(&canvas, &event);
1499 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
1500 QCOMPARE(input->text(), QString("My Hello world!"));
1502 input->setCursorPosition(2);
1503 event.setCommitString("Your", -2, 2);
1504 QGuiApplication::sendEvent(&canvas, &event);
1505 QCOMPARE(input->text(), QString("Your Hello world!"));
1506 QCOMPARE(input->cursorPosition(), 4);
1508 input->setCursorPosition(7);
1509 event.setCommitString("Goodbye", -2, 5);
1510 QGuiApplication::sendEvent(&canvas, &event);
1511 QCOMPARE(input->text(), QString("Your Goodbye world!"));
1512 QCOMPARE(input->cursorPosition(), 12);
1514 input->setCursorPosition(8);
1515 event.setCommitString("Our", -8, 4);
1516 QGuiApplication::sendEvent(&canvas, &event);
1517 QCOMPARE(input->text(), QString("Our Goodbye world!"));
1518 QCOMPARE(input->cursorPosition(), 7);
1522 TextInput element should only handle left/right keys until the cursor reaches
1523 the extent of the text, then they should ignore the keys.
1526 void tst_qquicktextinput::navigation()
1528 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1530 canvas.requestActivateWindow();
1532 QVERIFY(canvas.rootObject() != 0);
1534 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1536 QVERIFY(input != 0);
1537 input->setCursorPosition(0);
1538 QTRY_VERIFY(input->hasActiveFocus() == true);
1539 simulateKey(&canvas, Qt::Key_Left);
1540 QVERIFY(input->hasActiveFocus() == false);
1541 simulateKey(&canvas, Qt::Key_Right);
1542 QVERIFY(input->hasActiveFocus() == true);
1543 //QT-2944: If text is selected, ensure we deselect upon cursor motion
1544 input->setCursorPosition(input->text().length());
1545 input->select(0,input->text().length());
1546 QVERIFY(input->selectionStart() != input->selectionEnd());
1547 simulateKey(&canvas, Qt::Key_Right);
1548 QVERIFY(input->selectionStart() == input->selectionEnd());
1549 QVERIFY(input->selectionStart() == input->text().length());
1550 QVERIFY(input->hasActiveFocus() == true);
1551 simulateKey(&canvas, Qt::Key_Right);
1552 QVERIFY(input->hasActiveFocus() == false);
1553 simulateKey(&canvas, Qt::Key_Left);
1554 QVERIFY(input->hasActiveFocus() == true);
1556 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1557 input->setCursorPosition(2);
1558 QCOMPARE(input->cursorPosition(),2);
1559 simulateKey(&canvas, Qt::Key_Up);
1560 QCOMPARE(input->cursorPosition(),2);
1561 simulateKey(&canvas, Qt::Key_Down);
1562 QCOMPARE(input->cursorPosition(),2);
1565 void tst_qquicktextinput::navigation_RTL()
1567 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1569 canvas.requestActivateWindow();
1571 QVERIFY(canvas.rootObject() != 0);
1573 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1575 QVERIFY(input != 0);
1576 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1577 input->setText(QString::fromUtf16(arabic_str, 11));
1579 input->setCursorPosition(0);
1580 QTRY_VERIFY(input->hasActiveFocus() == true);
1583 simulateKey(&canvas, Qt::Key_Right);
1584 QVERIFY(input->hasActiveFocus() == false);
1587 simulateKey(&canvas, Qt::Key_Left);
1588 QVERIFY(input->hasActiveFocus() == true);
1590 input->setCursorPosition(input->text().length());
1591 QVERIFY(input->hasActiveFocus() == true);
1594 simulateKey(&canvas, Qt::Key_Left);
1595 QVERIFY(input->hasActiveFocus() == false);
1598 simulateKey(&canvas, Qt::Key_Right);
1599 QVERIFY(input->hasActiveFocus() == true);
1602 void tst_qquicktextinput::copyAndPaste() {
1603 #ifndef QT_NO_CLIPBOARD
1607 PasteboardRef pasteboard;
1608 OSStatus status = PasteboardCreate(0, &pasteboard);
1609 if (status == noErr)
1610 CFRelease(pasteboard);
1612 QSKIP("This machine doesn't support the clipboard");
1616 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1617 QDeclarativeComponent textInputComponent(&engine);
1618 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1619 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1620 QVERIFY(textInput != 0);
1623 QCOMPARE(textInput->text().length(), 12);
1624 textInput->select(0, textInput->text().length());;
1626 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1627 QCOMPARE(textInput->selectedText().length(), 12);
1628 textInput->setCursorPosition(0);
1629 QVERIFY(textInput->canPaste());
1631 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1632 QCOMPARE(textInput->text().length(), 24);
1635 QVERIFY(textInput->canPaste());
1636 textInput->setReadOnly(true);
1637 QVERIFY(!textInput->canPaste());
1638 textInput->setReadOnly(false);
1639 QVERIFY(textInput->canPaste());
1642 textInput->setCursorPosition(0);
1643 textInput->selectWord();
1644 QCOMPARE(textInput->selectedText(), QString("Hello"));
1646 // select all and cut
1647 textInput->selectAll();
1649 QCOMPARE(textInput->text().length(), 0);
1651 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1652 QCOMPARE(textInput->text().length(), 24);
1654 // clear copy buffer
1655 QClipboard *clipboard = QGuiApplication::clipboard();
1658 QVERIFY(!textInput->canPaste());
1660 // test that copy functionality is disabled
1661 // when echo mode is set to hide text/password mode
1664 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
1665 textInput->setEchoMode(echoMode);
1666 textInput->setText("My password");
1667 textInput->select(0, textInput->text().length());;
1669 if (echoMode == QQuickTextInput::Normal) {
1670 QVERIFY(!clipboard->text().isEmpty());
1671 QCOMPARE(clipboard->text(), QString("My password"));
1674 QVERIFY(clipboard->text().isEmpty());
1683 void tst_qquicktextinput::canPasteEmpty() {
1684 #ifndef QT_NO_CLIPBOARD
1686 QGuiApplication::clipboard()->clear();
1688 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1689 QDeclarativeComponent textInputComponent(&engine);
1690 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1691 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1692 QVERIFY(textInput != 0);
1695 bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1696 QCOMPARE(textInput->canPaste(), cp);
1701 void tst_qquicktextinput::canPaste() {
1702 #ifndef QT_NO_CLIPBOARD
1704 QGuiApplication::clipboard()->setText("Some text");
1706 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1707 QDeclarativeComponent textInputComponent(&engine);
1708 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1709 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1710 QVERIFY(textInput != 0);
1713 bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1714 QCOMPARE(textInput->canPaste(), cp);
1719 void tst_qquicktextinput::passwordCharacter()
1721 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
1722 QDeclarativeComponent textInputComponent(&engine);
1723 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1724 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1725 QVERIFY(textInput != 0);
1727 textInput->setPasswordCharacter("X");
1728 qreal implicitWidth = textInput->implicitWidth();
1729 textInput->setPasswordCharacter(".");
1731 // QTBUG-12383 content is updated and redrawn
1732 QVERIFY(textInput->implicitWidth() < implicitWidth);
1737 void tst_qquicktextinput::cursorDelegate()
1739 QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorTest.qml")));
1741 view.requestActivateWindow();
1742 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
1743 QVERIFY(textInputObject != 0);
1744 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
1745 //Test Delegate gets created
1746 textInputObject->setFocus(true);
1747 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
1748 QVERIFY(delegateObject);
1749 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1750 //Test Delegate gets moved
1751 for (int i=0; i<= textInputObject->text().length(); i++) {
1752 textInputObject->setCursorPosition(i);
1753 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1754 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1756 textInputObject->setCursorPosition(0);
1757 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1758 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1759 //Test Delegate gets deleted
1760 textInputObject->setCursorDelegate(0);
1761 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
1764 void tst_qquicktextinput::cursorVisible()
1766 QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorVisible.qml")));
1768 view.requestActivateWindow();
1769 QTest::qWaitForWindowShown(&view);
1770 QTRY_COMPARE(&view, qGuiApp->focusWindow());
1772 QQuickTextInput input;
1773 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
1775 QCOMPARE(input.isCursorVisible(), false);
1777 input.setCursorVisible(true);
1778 QCOMPARE(input.isCursorVisible(), true);
1779 QCOMPARE(spy.count(), 1);
1781 input.setCursorVisible(false);
1782 QCOMPARE(input.isCursorVisible(), false);
1783 QCOMPARE(spy.count(), 2);
1785 input.setFocus(true);
1786 QCOMPARE(input.isCursorVisible(), false);
1787 QCOMPARE(spy.count(), 2);
1789 input.setParentItem(view.rootObject());
1790 QCOMPARE(input.isCursorVisible(), true);
1791 QCOMPARE(spy.count(), 3);
1793 input.setFocus(false);
1794 QCOMPARE(input.isCursorVisible(), false);
1795 QCOMPARE(spy.count(), 4);
1797 input.setFocus(true);
1798 QCOMPARE(input.isCursorVisible(), true);
1799 QCOMPARE(spy.count(), 5);
1801 QQuickView alternateView;
1802 alternateView.show();
1803 alternateView.requestActivateWindow();
1804 QTest::qWaitForWindowShown(&alternateView);
1806 QCOMPARE(input.isCursorVisible(), false);
1807 QCOMPARE(spy.count(), 6);
1809 view.requestActivateWindow();
1810 QTest::qWaitForWindowShown(&view);
1811 QCOMPARE(input.isCursorVisible(), true);
1812 QCOMPARE(spy.count(), 7);
1815 void tst_qquicktextinput::cursorRectangle()
1817 QSKIP("QTBUG-21689");
1819 QString text = "Hello World!";
1821 QQuickTextInput input;
1822 input.setText(text);
1823 QFontMetricsF fm(input.font());
1824 input.setWidth(fm.width(text.mid(0, 5)));
1828 // some tolerance for different fonts.
1830 const int error = 2;
1832 const int error = 5;
1836 for (int i = 0; i <= 5; ++i) {
1837 input.setCursorPosition(i);
1838 r = input.cursorRectangle();
1839 int textWidth = fm.width(text.mid(0, i));
1841 QVERIFY(r.left() < textWidth + error);
1842 QVERIFY(r.right() > textWidth - error);
1843 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1846 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
1847 QVERIFY(r.left() < input.boundingRect().width());
1848 QVERIFY(r.right() >= input.width() - error);
1850 for (int i = 6; i < text.length(); ++i) {
1851 input.setCursorPosition(i);
1852 QCOMPARE(r, input.cursorRectangle());
1853 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1856 for (int i = text.length() - 2; i >= 0; --i) {
1857 input.setCursorPosition(i);
1858 r = input.cursorRectangle();
1859 QVERIFY(r.right() >= 0);
1860 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1863 input.setText("Hi!");
1864 input.setHAlign(QQuickTextInput::AlignRight);
1865 r = input.cursorRectangle();
1866 QVERIFY(r.left() < input.boundingRect().width());
1867 QVERIFY(r.right() >= input.width() - error);
1870 void tst_qquicktextinput::readOnly()
1872 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("readOnly.qml")));
1874 canvas.requestActivateWindow();
1876 QVERIFY(canvas.rootObject() != 0);
1878 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1880 QVERIFY(input != 0);
1881 QTRY_VERIFY(input->hasActiveFocus() == true);
1882 QVERIFY(input->isReadOnly() == true);
1883 QString initial = input->text();
1884 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
1885 simulateKey(&canvas, k);
1886 simulateKey(&canvas, Qt::Key_Return);
1887 simulateKey(&canvas, Qt::Key_Space);
1888 simulateKey(&canvas, Qt::Key_Escape);
1889 QCOMPARE(input->text(), initial);
1891 input->setCursorPosition(3);
1892 input->setReadOnly(false);
1893 QCOMPARE(input->isReadOnly(), false);
1894 QCOMPARE(input->cursorPosition(), input->text().length());
1897 void tst_qquicktextinput::echoMode()
1899 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("echoMode.qml")));
1901 canvas.requestActivateWindow();
1902 QTest::qWaitForWindowShown(&canvas);
1903 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1905 QVERIFY(canvas.rootObject() != 0);
1907 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1909 QVERIFY(input != 0);
1910 QTRY_VERIFY(input->hasActiveFocus() == true);
1911 QString initial = input->text();
1912 Qt::InputMethodHints ref;
1913 QCOMPARE(initial, QLatin1String("ABCDefgh"));
1914 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
1915 QCOMPARE(input->displayText(), input->text());
1917 ref &= ~Qt::ImhHiddenText;
1918 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1919 QCOMPARE(input->inputMethodHints(), ref);
1920 input->setEchoMode(QQuickTextInput::NoEcho);
1921 QCOMPARE(input->text(), initial);
1922 QCOMPARE(input->displayText(), QLatin1String(""));
1923 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
1925 ref |= Qt::ImhHiddenText;
1926 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1927 QCOMPARE(input->inputMethodHints(), ref);
1928 input->setEchoMode(QQuickTextInput::Password);
1930 ref |= Qt::ImhHiddenText;
1931 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1932 QCOMPARE(input->text(), initial);
1933 QCOMPARE(input->displayText(), QLatin1String("********"));
1934 QCOMPARE(input->inputMethodHints(), ref);
1935 input->setPasswordCharacter(QChar('Q'));
1936 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
1937 QCOMPARE(input->text(), initial);
1938 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1939 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
1940 //PasswordEchoOnEdit
1941 ref &= ~Qt::ImhHiddenText;
1942 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1943 QCOMPARE(input->inputMethodHints(), ref);
1944 QCOMPARE(input->text(), initial);
1945 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1946 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
1947 QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
1948 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1949 QCOMPARE(input->text(), QLatin1String("a"));
1950 QCOMPARE(input->displayText(), QLatin1String("a"));
1951 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
1952 input->setFocus(false);
1953 QVERIFY(input->hasActiveFocus() == false);
1954 QCOMPARE(input->displayText(), QLatin1String("Q"));
1955 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
1956 input->setFocus(true);
1957 QVERIFY(input->hasActiveFocus());
1958 QInputMethodEvent inputEvent;
1959 inputEvent.setCommitString(initial);
1960 QGuiApplication::sendEvent(input, &inputEvent);
1961 QCOMPARE(input->text(), initial);
1962 QCOMPARE(input->displayText(), initial);
1963 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
1966 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
1967 void tst_qdeclarativetextinput::passwordEchoDelay()
1969 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("echoMode.qml")));
1972 QGuiApplication::setActiveWindow(&canvas);
1973 QTest::qWaitForWindowShown(&canvas);
1974 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1976 QVERIFY(canvas.rootObject() != 0);
1978 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1980 QChar fillChar = QLatin1Char('*');
1982 input->setEchoMode(QDeclarativeTextInput::Password);
1983 QCOMPARE(input->displayText(), QString(8, fillChar));
1984 input->setText(QString());
1985 QCOMPARE(input->displayText(), QString());
1987 QTest::keyPress(&canvas, '0');
1988 QTest::keyPress(&canvas, '1');
1989 QTest::keyPress(&canvas, '2');
1990 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
1991 QTest::keyPress(&canvas, '3');
1992 QTest::keyPress(&canvas, '4');
1993 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1994 QTest::keyPress(&canvas, Qt::Key_Backspace);
1995 QCOMPARE(input->displayText(), QString(4, fillChar));
1996 QTest::keyPress(&canvas, '4');
1997 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1998 QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
1999 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2000 QTest::keyPress(&canvas, '5');
2001 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2002 input->setFocus(false);
2003 QVERIFY(!input->hasFocus());
2004 QCOMPARE(input->displayText(), QString(6, fillChar));
2005 input->setFocus(true);
2006 QTRY_VERIFY(input->hasFocus());
2007 QCOMPARE(input->displayText(), QString(6, fillChar));
2008 QTest::keyPress(&canvas, '6');
2009 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2011 QInputMethodEvent ev;
2012 ev.setCommitString(QLatin1String("7"));
2013 QGuiApplication::sendEvent(&canvas, &ev);
2014 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2019 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2021 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2022 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2024 QGuiApplication::sendEvent(view, &press);
2025 QGuiApplication::sendEvent(view, &release);
2029 class MyInputContext : public QInputContext
2032 MyInputContext() : updateReceived(false), eventType(QEvent::None) {}
2033 ~MyInputContext() {}
2035 QString identifierName() { return QString(); }
2036 QString language() { return QString(); }
2040 bool isComposing() const { return false; }
2042 void update() { updateReceived = true; }
2044 void mouseHandler(int x, QMouseEvent *event)
2047 eventType = event->type();
2048 eventPosition = event->pos();
2049 eventGlobalPosition = event->globalPos();
2050 eventButton = event->button();
2051 eventButtons = event->buttons();
2052 eventModifiers = event->modifiers();
2055 void sendPreeditText(const QString &text, int cursor)
2057 QList<QInputMethodEvent::Attribute> attributes;
2058 attributes.append(QInputMethodEvent::Attribute(
2059 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2061 QInputMethodEvent event(text, attributes);
2065 bool updateReceived;
2067 QEvent::Type eventType;
2068 QPoint eventPosition;
2069 QPoint eventGlobalPosition;
2070 Qt::MouseButton eventButton;
2071 Qt::MouseButtons eventButtons;
2072 Qt::KeyboardModifiers eventModifiers;
2076 void tst_qquicktextinput::openInputPanel()
2078 QQuickView view(QUrl::fromLocalFile(TESTDATA("openInputPanel.qml")));
2080 view.requestActivateWindow();
2081 QTest::qWaitForWindowShown(&view);
2082 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2084 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2087 // check default values
2088 QVERIFY(input->focusOnPress());
2089 QVERIFY(!input->hasActiveFocus());
2090 qDebug() << &input << qApp->inputPanel()->inputItem();
2091 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2092 QEXPECT_FAIL("", "QTBUG-21946", Abort);
2093 QCOMPARE(qApp->inputPanel()->visible(), false);
2095 // input panel should open on focus
2096 QPoint centerPoint(view.width()/2, view.height()/2);
2097 Qt::KeyboardModifiers noModifiers = 0;
2098 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2099 QGuiApplication::processEvents();
2100 QVERIFY(input->hasActiveFocus());
2101 QCOMPARE(qApp->inputPanel()->inputItem(), input);
2102 QCOMPARE(qApp->inputPanel()->visible(), true);
2103 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2105 // input panel should be re-opened when pressing already focused TextInput
2106 qApp->inputPanel()->hide();
2107 QCOMPARE(qApp->inputPanel()->visible(), false);
2108 QVERIFY(input->hasActiveFocus());
2109 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2110 QGuiApplication::processEvents();
2111 QCOMPARE(qApp->inputPanel()->visible(), true);
2112 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2114 // input panel should stay visible if focus is lost to another text inputor
2115 QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2116 QQuickTextInput anotherInput;
2117 anotherInput.setParentItem(view.rootObject());
2118 anotherInput.setFocus(true);
2119 QCOMPARE(qApp->inputPanel()->visible(), true);
2120 QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2121 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2123 anotherInput.setFocus(false);
2124 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2125 QCOMPARE(view.activeFocusItem(), view.rootItem());
2126 anotherInput.setFocus(true);
2128 // input item should be null if focus is lost to an item that doesn't accept inputs
2130 item.setParentItem(view.rootObject());
2131 item.setFocus(true);
2132 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2133 QCOMPARE(view.activeFocusItem(), &item);
2135 qApp->inputPanel()->hide();
2137 // input panel should not be opened if TextInput is read only
2138 input->setReadOnly(true);
2139 input->setFocus(true);
2140 QCOMPARE(qApp->inputPanel()->visible(), false);
2141 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2142 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2143 QGuiApplication::processEvents();
2144 QCOMPARE(qApp->inputPanel()->visible(), false);
2146 // input panel should not be opened if focusOnPress is set to false
2147 input->setFocusOnPress(false);
2148 input->setFocus(false);
2149 input->setFocus(true);
2150 QCOMPARE(qApp->inputPanel()->visible(), false);
2151 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2152 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2153 QCOMPARE(qApp->inputPanel()->visible(), false);
2155 // input panel should open when openSoftwareInputPanel is called
2156 input->openSoftwareInputPanel();
2157 QCOMPARE(qApp->inputPanel()->visible(), true);
2159 // input panel should close when closeSoftwareInputPanel is called
2160 input->closeSoftwareInputPanel();
2161 QCOMPARE(qApp->inputPanel()->visible(), false);
2164 class MyTextInput : public QQuickTextInput
2167 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2171 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2174 return QQuickTextInput::updatePaintNode(node, data);
2179 void tst_qquicktextinput::setHAlignClearCache()
2183 input.setText("Hello world");
2184 input.setParentItem(view.rootItem());
2186 view.requestActivateWindow();
2187 QTest::qWaitForWindowShown(&view);
2188 QTRY_COMPARE(input.nbPaint, 1);
2189 input.setHAlign(QQuickTextInput::AlignRight);
2190 //Changing the alignment should trigger a repaint
2191 QTRY_COMPARE(input.nbPaint, 2);
2194 void tst_qquicktextinput::focusOutClearSelection()
2197 QQuickTextInput input;
2198 QQuickTextInput input2;
2199 input.setText(QLatin1String("Hello world"));
2200 input.setFocus(true);
2201 input2.setParentItem(view.rootItem());
2202 input.setParentItem(view.rootItem());
2204 view.requestActivateWindow();
2205 QTest::qWaitForWindowShown(&view);
2207 //The selection should work
2208 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2209 input2.setFocus(true);
2210 QGuiApplication::processEvents();
2211 //The input lost the focus selection should be cleared
2212 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2215 void tst_qquicktextinput::geometrySignals()
2217 QDeclarativeComponent component(&engine, TESTDATA("geometrySignals.qml"));
2218 QObject *o = component.create();
2220 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2221 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2225 void tst_qquicktextinput::testQtQuick11Attributes()
2227 QFETCH(QString, code);
2228 QFETCH(QString, warning);
2229 QFETCH(QString, error);
2231 QDeclarativeEngine engine;
2234 QDeclarativeComponent valid(&engine);
2235 valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2236 obj = valid.create();
2238 QVERIFY(valid.errorString().isEmpty());
2241 QDeclarativeComponent invalid(&engine);
2242 invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2243 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2244 obj = invalid.create();
2245 QCOMPARE(invalid.errorString(), error);
2249 void tst_qquicktextinput::testQtQuick11Attributes_data()
2251 QTest::addColumn<QString>("code");
2252 QTest::addColumn<QString>("warning");
2253 QTest::addColumn<QString>("error");
2255 QTest::newRow("canPaste") << "property bool foo: canPaste"
2256 << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2259 QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2260 << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2263 QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2264 << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2268 void tst_qquicktextinput::preeditAutoScroll()
2271 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2274 QString preeditText = "califragisiticexpialidocious!";
2276 QQuickView view(QUrl::fromLocalFile(TESTDATA("preeditAutoScroll.qml")));
2278 // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2279 // and QWidget won't allow an input context to be set when the flag is not set.
2280 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2281 view.setInputContext(&ic);
2282 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2284 view.requestActivateWindow();
2285 QTest::qWaitForWindowShown(&view);
2286 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2287 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2290 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2291 int cursorRectangleChanges = 0;
2293 QFontMetricsF fm(input->font());
2294 input->setWidth(fm.width(input->text()));
2296 // test the text is scrolled so the preedit is visible.
2297 ic.sendPreeditText(preeditText.mid(0, 3), 1);
2298 QVERIFY(input->positionAt(0) != 0);
2299 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2300 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2302 // test the text is scrolled back when the preedit is removed.
2303 ic.sendEvent(QInputMethodEvent());
2304 QCOMPARE(input->positionAt(0), 0);
2305 QCOMPARE(input->positionAt(input->width()), 5);
2306 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2308 // some tolerance for different fonts.
2310 const int error = 2;
2312 const int error = 5;
2315 // test if the preedit is larger than the text input that the
2316 // character preceding the cursor is still visible.
2317 qreal x = input->positionToRectangle(0).x();
2318 for (int i = 0; i < 3; ++i) {
2319 ic.sendPreeditText(preeditText, i + 1);
2320 QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2321 QVERIFY(input->positionToRectangle(0).x() < x);
2322 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2323 x = input->positionToRectangle(0).x();
2325 for (int i = 1; i >= 0; --i) {
2326 ic.sendPreeditText(preeditText, i + 1);
2327 QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2328 QVERIFY(input->positionToRectangle(0).x() > x);
2329 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2330 x = input->positionToRectangle(0).x();
2333 // Test incrementing the preedit cursor doesn't cause further
2334 // scrolling when right most text is visible.
2335 ic.sendPreeditText(preeditText, preeditText.length() - 3);
2336 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2337 x = input->positionToRectangle(0).x();
2338 for (int i = 2; i >= 0; --i) {
2339 ic.sendPreeditText(preeditText, preeditText.length() - i);
2340 QCOMPARE(input->positionToRectangle(0).x(), x);
2341 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2343 for (int i = 1; i < 3; ++i) {
2344 ic.sendPreeditText(preeditText, preeditText.length() - i);
2345 QCOMPARE(input->positionToRectangle(0).x(), x);
2346 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2349 // Test disabling auto scroll.
2350 ic.sendEvent(QInputMethodEvent());
2352 input->setAutoScroll(false);
2353 ic.sendPreeditText(preeditText.mid(0, 3), 1);
2354 QCOMPARE(input->positionAt(0), 0);
2355 QCOMPARE(input->positionAt(input->width()), 5);
2359 void tst_qquicktextinput::preeditMicroFocus()
2362 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2365 QString preeditText = "super";
2367 QQuickView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml")));
2369 // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2370 // and QWidget won't allow an input context to be set when the flag is not set.
2371 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2372 view.setInputContext(&ic);
2373 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2375 view.requestActivateWindow();
2376 QTest::qWaitForWindowShown(&view);
2377 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2378 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2382 QRect previousRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2384 // Verify that the micro focus rect is positioned the same for position 0 as
2385 // it would be if there was no preedit text.
2386 ic.updateReceived = false;
2387 ic.sendPreeditText(preeditText, 0);
2388 currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2389 QCOMPARE(currentRect, previousRect);
2390 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2391 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());
2401 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2402 QCOMPARE(ic.updateReceived, true);
2404 previousRect = currentRect;
2407 // Verify that if there is no preedit cursor then the micro focus rect is the
2408 // same as it would be if it were positioned at the end of the preedit text.
2409 ic.sendPreeditText(preeditText, 0);
2410 ic.updateReceived = false;
2411 ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2412 currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2413 QCOMPARE(currentRect, previousRect);
2414 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2415 QCOMPARE(ic.updateReceived, true);
2420 void tst_qquicktextinput::inputContextMouseHandler()
2423 QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2426 QString text = "supercalifragisiticexpialidocious!";
2428 QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2430 // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2431 // and QWidget won't allow an input context to be set when the flag is not set.
2432 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2433 view.setInputContext(&ic);
2434 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2436 view.requestActivateWindow();
2437 QTest::qWaitForWindowShown(&view);
2438 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2439 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2442 QFontMetricsF fm(input->font());
2443 const qreal y = fm.height() / 2;
2445 QPoint position2 = input->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
2446 QPoint position8 = input->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
2447 QPoint position20 = input->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
2448 QPoint position27 = input->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
2449 QPoint globalPosition2 = view.mapToGlobal(position2);
2450 QPoint globalposition8 = view.mapToGlobal(position8);
2451 QPoint globalposition20 = view.mapToGlobal(position20);
2452 QPoint globalposition27 = view.mapToGlobal(position27);
2454 ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2456 QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
2457 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2458 QCOMPARE(ic.eventPosition, position2);
2459 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2460 QCOMPARE(ic.eventButton, Qt::LeftButton);
2461 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2462 QVERIFY(ic.cursor < 0);
2463 ic.eventType = QEvent::None;
2465 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
2466 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2467 QCOMPARE(ic.eventPosition, position2);
2468 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2469 QCOMPARE(ic.eventButton, Qt::LeftButton);
2470 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2471 QVERIFY(ic.cursor < 0);
2472 ic.eventType = QEvent::None;
2474 { QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2475 QGuiApplication::sendEvent(&view, &mv); }
2476 QCOMPARE(ic.eventType, QEvent::None);
2478 { QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2479 QGuiApplication::sendEvent(&view, &mv); }
2480 QCOMPARE(ic.eventType, QEvent::MouseMove);
2481 QCOMPARE(ic.eventPosition, position27);
2482 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2483 QCOMPARE(ic.eventButton, Qt::LeftButton);
2484 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2485 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); // 15 is expected but some platforms may be off by one.
2486 ic.eventType = QEvent::None;
2488 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
2489 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2490 QCOMPARE(ic.eventPosition, position27);
2491 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2492 QCOMPARE(ic.eventButton, Qt::LeftButton);
2493 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2494 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2495 ic.eventType = QEvent::None;
2497 // And in the other direction.
2498 QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
2499 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2500 QCOMPARE(ic.eventPosition, position27);
2501 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2502 QCOMPARE(ic.eventButton, Qt::LeftButton);
2503 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2504 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2505 ic.eventType = QEvent::None;
2507 QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
2508 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2509 QCOMPARE(ic.eventPosition, position27);
2510 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2511 QCOMPARE(ic.eventButton, Qt::RightButton);
2512 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2513 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2514 ic.eventType = QEvent::None;
2516 { QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2517 QGuiApplication::sendEvent(&view, &mv); }
2518 QCOMPARE(ic.eventType, QEvent::MouseMove);
2519 QCOMPARE(ic.eventPosition, position20);
2520 QCOMPARE(ic.eventGlobalPosition, globalposition20);
2521 QCOMPARE(ic.eventButton, Qt::RightButton);
2522 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2523 QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2524 ic.eventType = QEvent::None;
2526 { QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2527 QGuiApplication::sendEvent(&view, &mv); }
2528 QCOMPARE(ic.eventType, QEvent::None);
2530 QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
2531 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2532 QCOMPARE(ic.eventPosition, position2);
2533 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2534 QCOMPARE(ic.eventButton, Qt::RightButton);
2535 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2536 QVERIFY(ic.cursor < 0);
2537 ic.eventType = QEvent::None;
2541 void tst_qquicktextinput::inputMethodComposing()
2543 QString text = "supercalifragisiticexpialidocious!";
2545 QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2547 view.requestActivateWindow();
2548 QTest::qWaitForWindowShown(&view);
2549 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2550 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2552 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2554 QCOMPARE(input->isInputMethodComposing(), false);
2556 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2557 QGuiApplication::sendEvent(input, &event);
2559 QCOMPARE(input->isInputMethodComposing(), true);
2560 QCOMPARE(spy.count(), 1);
2563 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2564 QGuiApplication::sendEvent(input, &event);
2566 QCOMPARE(spy.count(), 1);
2569 QInputMethodEvent event;
2570 QGuiApplication::sendEvent(input, &event);
2572 QCOMPARE(input->isInputMethodComposing(), false);
2573 QCOMPARE(spy.count(), 2);
2576 void tst_qquicktextinput::cursorRectangleSize()
2578 QQuickView *canvas = new QQuickView(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
2579 QVERIFY(canvas->rootObject() != 0);
2581 canvas->requestActivateWindow();
2582 QTest::qWaitForWindowShown(canvas);
2584 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
2585 QVERIFY(textInput != 0);
2586 textInput->setFocus(Qt::OtherFocusReason);
2587 QRectF cursorRect = textInput->positionToRectangle(textInput->cursorPosition());
2588 QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImCursorRectangle).toRectF();
2589 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2590 qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
2592 QRectF microFocusFromApp = event.value(Qt::ImCursorRectangle).toRectF();
2594 QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2595 QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2600 void tst_qquicktextinput::tripleClickSelectsAll()
2602 QString qmlfile = TESTDATA("positionAt.qml");
2603 QQuickView view(QUrl::fromLocalFile(qmlfile));
2605 view.requestActivateWindow();
2606 QTest::qWaitForWindowShown(&view);
2608 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2610 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
2613 QLatin1String hello("Hello world!");
2614 input->setSelectByMouse(true);
2615 input->setText(hello);
2617 // Clicking on the same point inside TextInput three times in a row
2618 // should trigger a triple click, thus selecting all the text.
2619 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
2620 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2621 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2622 QGuiApplication::processEvents();
2623 QCOMPARE(input->selectedText(), hello);
2625 // Now it simulates user moving the mouse between the second and the third click.
2626 // In this situation, we don't expect a triple click.
2627 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
2628 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2629 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
2630 QGuiApplication::processEvents();
2631 QVERIFY(input->selectedText().isEmpty());
2633 // And now we press the third click too late, so no triple click event is triggered.
2634 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2635 QGuiApplication::processEvents();
2636 QTest::qWait(QApplication::doubleClickInterval() + 1);
2637 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2638 QGuiApplication::processEvents();
2639 QVERIFY(input->selectedText().isEmpty());
2642 void tst_qquicktextinput::QTBUG_19956_data()
2644 QTest::addColumn<QString>("url");
2645 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
2646 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
2649 void tst_qquicktextinput::QTBUG_19956()
2651 QFETCH(QString, url);
2653 QQuickView canvas(QUrl::fromLocalFile(TESTDATA(url)));
2655 canvas.requestActivateWindow();
2656 QTest::qWaitForWindowShown(&canvas);
2657 QVERIFY(canvas.rootObject() != 0);
2658 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
2660 input->setFocus(true);
2661 QVERIFY(input->hasActiveFocus());
2663 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
2664 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
2665 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
2666 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2668 canvas.rootObject()->setProperty("topvalue", 15);
2669 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
2670 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2672 canvas.rootObject()->setProperty("topvalue", 25);
2673 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
2674 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2676 canvas.rootObject()->setProperty("bottomvalue", 21);
2677 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
2678 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2680 canvas.rootObject()->setProperty("bottomvalue", 10);
2681 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
2682 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2685 void tst_qquicktextinput::QTBUG_19956_regexp()
2687 QQuickView canvas(QUrl::fromLocalFile(TESTDATA("qtbug-19956regexp.qml")));
2689 canvas.requestActivateWindow();
2690 QTest::qWaitForWindowShown(&canvas);
2691 QVERIFY(canvas.rootObject() != 0);
2692 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
2694 input->setFocus(true);
2695 QVERIFY(input->hasActiveFocus());
2697 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
2698 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
2699 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
2700 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2702 canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
2703 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
2704 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2706 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
2707 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
2708 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2711 QTEST_MAIN(tst_qquicktextinput)
2713 #include "tst_qquicktextinput.moc"