1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtTest/QSignalSpy>
43 #include "../../../shared/util.h"
44 #include <QtDeclarative/qdeclarativeengine.h>
46 #include <QtDeclarative/qsgview.h>
47 #include <private/qsgtextinput_p.h>
48 #include <private/qsgtextinput_p_p.h>
52 #include <QInputContext>
53 #include <private/qapplication_p.h>
54 #include <private/qsgdistancefieldglyphcache_p.h>
55 #include <QtOpenGL/QGLShaderProgram>
58 #include "qplatformdefs.h"
61 // In Symbian OS test data is located in applications private dir
65 Q_DECLARE_METATYPE(QSGTextInput::SelectionMode)
66 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
68 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
70 // XXX This will be replaced by some clever persistent platform image store.
71 QString persistent_dir = SRCDIR "/data";
72 QString arch = "unknown-architecture"; // QTest needs to help with this.
74 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
76 if (!QFile::exists(expectfile)) {
77 actual.save(expectfile);
78 qWarning() << "created" << expectfile;
84 class tst_qsgtextinput : public QObject
93 void cleanupTestCase();
99 void isRightToLeft_data();
100 void isRightToLeft();
101 void moveCursorSelection_data();
102 void moveCursorSelection();
103 void moveCursorSelectionSequence_data();
104 void moveCursorSelectionSequence();
105 void dragMouseSelection();
106 void mouseSelectionMode_data();
107 void mouseSelectionMode();
109 void horizontalAlignment_data();
110 void horizontalAlignment();
111 void horizontalAlignment_RightToLeft();
120 void passwordCharacter();
121 void cursorDelegate();
122 void cursorVisible();
123 void cursorRectangle();
125 void navigation_RTL();
127 void canPasteEmpty();
131 void openInputPanelOnClick();
132 void openInputPanelOnFocus();
133 void setHAlignClearCache();
134 void focusOutClearSelection();
137 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
138 void passwordEchoDelay();
140 void geometrySignals();
141 void testQtQuick11Attributes();
142 void testQtQuick11Attributes_data();
144 void preeditAutoScroll();
145 void preeditMicroFocus();
146 void inputContextMouseHandler();
147 void inputMethodComposing();
148 void cursorRectangleSize();
151 void simulateKey(QSGView *, int key);
153 QDeclarativeEngine engine;
154 QStringList standard;
155 QStringList colorStrings;
157 void tst_qsgtextinput::initTestCase()
160 if (!QGLShaderProgram::hasOpenGLShaderPrograms(canvas.context()))
161 QSKIP("TextInput item needs OpenGL 2.0", SkipAll);
164 void tst_qsgtextinput::cleanupTestCase()
168 tst_qsgtextinput::tst_qsgtextinput()
170 standard << "the quick brown fox jumped over the lazy dog"
171 << "It's supercalifragisiticexpialidocious!"
176 colorStrings << "aliceblue"
190 void tst_qsgtextinput::text()
193 QDeclarativeComponent textinputComponent(&engine);
194 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
195 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
197 QVERIFY(textinputObject != 0);
198 QCOMPARE(textinputObject->text(), QString(""));
200 delete textinputObject;
203 for (int i = 0; i < standard.size(); i++)
205 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
206 QDeclarativeComponent textinputComponent(&engine);
207 textinputComponent.setData(componentStr.toLatin1(), QUrl());
208 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
210 QVERIFY(textinputObject != 0);
211 QCOMPARE(textinputObject->text(), standard.at(i));
213 delete textinputObject;
218 void tst_qsgtextinput::width()
220 // uses Font metrics to find the width for standard
222 QDeclarativeComponent textinputComponent(&engine);
223 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
224 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
226 QVERIFY(textinputObject != 0);
227 QCOMPARE(textinputObject->width(), 0.0);
229 delete textinputObject;
232 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
234 for (int i = 0; i < standard.size(); i++)
237 qreal metricWidth = 0.0;
238 if (requiresUnhintedMetrics) {
239 QString s = standard.at(i);
240 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
242 QTextLayout layout(s);
243 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
246 option.setUseDesignMetrics(true);
247 layout.setTextOption(option);
250 layout.beginLayout();
252 QTextLine line = layout.createLine();
259 metricWidth = ceil(layout.boundingRect().width());
262 metricWidth = fm.width(standard.at(i));
265 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
266 QDeclarativeComponent textinputComponent(&engine);
267 textinputComponent.setData(componentStr.toLatin1(), QUrl());
268 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
270 QVERIFY(textinputObject != 0);
271 int delta = abs(int(int(textinputObject->width()) - metricWidth));
272 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
274 delete textinputObject;
278 void tst_qsgtextinput::font()
280 //test size, then bold, then italic, then family
282 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
283 QDeclarativeComponent textinputComponent(&engine);
284 textinputComponent.setData(componentStr.toLatin1(), QUrl());
285 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
287 QVERIFY(textinputObject != 0);
288 QCOMPARE(textinputObject->font().pointSize(), 40);
289 QCOMPARE(textinputObject->font().bold(), false);
290 QCOMPARE(textinputObject->font().italic(), false);
292 delete textinputObject;
296 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
297 QDeclarativeComponent textinputComponent(&engine);
298 textinputComponent.setData(componentStr.toLatin1(), QUrl());
299 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
301 QVERIFY(textinputObject != 0);
302 QCOMPARE(textinputObject->font().bold(), true);
303 QCOMPARE(textinputObject->font().italic(), false);
305 delete textinputObject;
309 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
310 QDeclarativeComponent textinputComponent(&engine);
311 textinputComponent.setData(componentStr.toLatin1(), QUrl());
312 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
314 QVERIFY(textinputObject != 0);
315 QCOMPARE(textinputObject->font().italic(), true);
316 QCOMPARE(textinputObject->font().bold(), false);
318 delete textinputObject;
322 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
323 QDeclarativeComponent textinputComponent(&engine);
324 textinputComponent.setData(componentStr.toLatin1(), QUrl());
325 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
327 QVERIFY(textinputObject != 0);
328 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
329 QCOMPARE(textinputObject->font().bold(), false);
330 QCOMPARE(textinputObject->font().italic(), false);
332 delete textinputObject;
336 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
337 QDeclarativeComponent textinputComponent(&engine);
338 textinputComponent.setData(componentStr.toLatin1(), QUrl());
339 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
341 QVERIFY(textinputObject != 0);
342 QCOMPARE(textinputObject->font().family(), QString(""));
344 delete textinputObject;
348 void tst_qsgtextinput::color()
351 for (int i = 0; i < colorStrings.size(); i++)
353 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
354 QDeclarativeComponent textinputComponent(&engine);
355 textinputComponent.setData(componentStr.toLatin1(), QUrl());
356 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
357 QVERIFY(textinputObject != 0);
358 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
360 delete textinputObject;
363 //test selection color
364 for (int i = 0; i < colorStrings.size(); i++)
366 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
367 QDeclarativeComponent textinputComponent(&engine);
368 textinputComponent.setData(componentStr.toLatin1(), QUrl());
369 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
370 QVERIFY(textinputObject != 0);
371 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
373 delete textinputObject;
376 //test selected text color
377 for (int i = 0; i < colorStrings.size(); i++)
379 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
380 QDeclarativeComponent textinputComponent(&engine);
381 textinputComponent.setData(componentStr.toLatin1(), QUrl());
382 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
383 QVERIFY(textinputObject != 0);
384 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
386 delete textinputObject;
390 QString colorStr = "#AA001234";
391 QColor testColor("#001234");
392 testColor.setAlpha(170);
394 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
395 QDeclarativeComponent textinputComponent(&engine);
396 textinputComponent.setData(componentStr.toLatin1(), QUrl());
397 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
399 QVERIFY(textinputObject != 0);
400 QCOMPARE(textinputObject->color(), testColor);
402 delete textinputObject;
406 void tst_qsgtextinput::selection()
408 QString testStr = standard[0];
409 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
410 QDeclarativeComponent textinputComponent(&engine);
411 textinputComponent.setData(componentStr.toLatin1(), QUrl());
412 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
413 QVERIFY(textinputObject != 0);
416 //Test selection follows cursor
417 for(int i=0; i<= testStr.size(); i++) {
418 textinputObject->setCursorPosition(i);
419 QCOMPARE(textinputObject->cursorPosition(), i);
420 QCOMPARE(textinputObject->selectionStart(), i);
421 QCOMPARE(textinputObject->selectionEnd(), i);
422 QVERIFY(textinputObject->selectedText().isNull());
425 textinputObject->setCursorPosition(0);
426 QVERIFY(textinputObject->cursorPosition() == 0);
427 QVERIFY(textinputObject->selectionStart() == 0);
428 QVERIFY(textinputObject->selectionEnd() == 0);
429 QVERIFY(textinputObject->selectedText().isNull());
431 // Verify invalid positions are ignored.
432 textinputObject->setCursorPosition(-1);
433 QVERIFY(textinputObject->cursorPosition() == 0);
434 QVERIFY(textinputObject->selectionStart() == 0);
435 QVERIFY(textinputObject->selectionEnd() == 0);
436 QVERIFY(textinputObject->selectedText().isNull());
438 textinputObject->setCursorPosition(textinputObject->text().count()+1);
439 QVERIFY(textinputObject->cursorPosition() == 0);
440 QVERIFY(textinputObject->selectionStart() == 0);
441 QVERIFY(textinputObject->selectionEnd() == 0);
442 QVERIFY(textinputObject->selectedText().isNull());
445 for(int i=0; i<= testStr.size(); i++) {
446 textinputObject->select(0,i);
447 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
449 for(int i=0; i<= testStr.size(); i++) {
450 textinputObject->select(i,testStr.size());
451 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
454 textinputObject->setCursorPosition(0);
455 QVERIFY(textinputObject->cursorPosition() == 0);
456 QVERIFY(textinputObject->selectionStart() == 0);
457 QVERIFY(textinputObject->selectionEnd() == 0);
458 QVERIFY(textinputObject->selectedText().isNull());
460 //Test Error Ignoring behaviour
461 textinputObject->setCursorPosition(0);
462 QVERIFY(textinputObject->selectedText().isNull());
463 textinputObject->select(-10,0);
464 QVERIFY(textinputObject->selectedText().isNull());
465 textinputObject->select(100,110);
466 QVERIFY(textinputObject->selectedText().isNull());
467 textinputObject->select(0,-10);
468 QVERIFY(textinputObject->selectedText().isNull());
469 textinputObject->select(0,100);
470 QVERIFY(textinputObject->selectedText().isNull());
471 textinputObject->select(0,10);
472 QVERIFY(textinputObject->selectedText().size() == 10);
473 textinputObject->select(-10,10);
474 QVERIFY(textinputObject->selectedText().size() == 10);
475 textinputObject->select(100,101);
476 QVERIFY(textinputObject->selectedText().size() == 10);
477 textinputObject->select(0,-10);
478 QVERIFY(textinputObject->selectedText().size() == 10);
479 textinputObject->select(0,100);
480 QVERIFY(textinputObject->selectedText().size() == 10);
482 textinputObject->deselect();
483 QVERIFY(textinputObject->selectedText().isNull());
484 textinputObject->select(0,10);
485 QVERIFY(textinputObject->selectedText().size() == 10);
486 textinputObject->deselect();
487 QVERIFY(textinputObject->selectedText().isNull());
489 delete textinputObject;
492 void tst_qsgtextinput::isRightToLeft_data()
494 QTest::addColumn<QString>("text");
495 QTest::addColumn<bool>("emptyString");
496 QTest::addColumn<bool>("firstCharacter");
497 QTest::addColumn<bool>("lastCharacter");
498 QTest::addColumn<bool>("middleCharacter");
499 QTest::addColumn<bool>("startString");
500 QTest::addColumn<bool>("midString");
501 QTest::addColumn<bool>("endString");
503 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
504 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
505 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
506 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
507 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
508 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;
509 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
512 void tst_qsgtextinput::isRightToLeft()
514 QFETCH(QString, text);
515 QFETCH(bool, emptyString);
516 QFETCH(bool, firstCharacter);
517 QFETCH(bool, lastCharacter);
518 QFETCH(bool, middleCharacter);
519 QFETCH(bool, startString);
520 QFETCH(bool, midString);
521 QFETCH(bool, endString);
523 QSGTextInput textInput;
524 textInput.setText(text);
526 // first test that the right string is delivered to the QString::isRightToLeft()
527 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
528 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
529 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
530 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
531 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
532 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
534 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
535 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
537 // then test that the feature actually works
538 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
539 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
540 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
541 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
542 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
543 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
545 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
546 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
549 void tst_qsgtextinput::moveCursorSelection_data()
551 QTest::addColumn<QString>("testStr");
552 QTest::addColumn<int>("cursorPosition");
553 QTest::addColumn<int>("movePosition");
554 QTest::addColumn<QSGTextInput::SelectionMode>("mode");
555 QTest::addColumn<int>("selectionStart");
556 QTest::addColumn<int>("selectionEnd");
557 QTest::addColumn<bool>("reversible");
559 // () contains the text selected by the cursor.
560 // <> contains the actual selection.
562 QTest::newRow("(t)he|characters")
563 << standard[0] << 0 << 1 << QSGTextInput::SelectCharacters << 0 << 1 << true;
564 QTest::newRow("do(g)|characters")
565 << standard[0] << 43 << 44 << QSGTextInput::SelectCharacters << 43 << 44 << true;
566 QTest::newRow("jum(p)ed|characters")
567 << standard[0] << 23 << 24 << QSGTextInput::SelectCharacters << 23 << 24 << true;
568 QTest::newRow("jumped( )over|characters")
569 << standard[0] << 26 << 27 << QSGTextInput::SelectCharacters << 26 << 27 << true;
570 QTest::newRow("(the )|characters")
571 << standard[0] << 0 << 4 << QSGTextInput::SelectCharacters << 0 << 4 << true;
572 QTest::newRow("( dog)|characters")
573 << standard[0] << 40 << 44 << QSGTextInput::SelectCharacters << 40 << 44 << true;
574 QTest::newRow("( jumped )|characters")
575 << standard[0] << 19 << 27 << QSGTextInput::SelectCharacters << 19 << 27 << true;
576 QTest::newRow("th(e qu)ick|characters")
577 << standard[0] << 2 << 6 << QSGTextInput::SelectCharacters << 2 << 6 << true;
578 QTest::newRow("la(zy d)og|characters")
579 << standard[0] << 38 << 42 << QSGTextInput::SelectCharacters << 38 << 42 << true;
580 QTest::newRow("jum(ped ov)er|characters")
581 << standard[0] << 23 << 29 << QSGTextInput::SelectCharacters << 23 << 29 << true;
582 QTest::newRow("()the|characters")
583 << standard[0] << 0 << 0 << QSGTextInput::SelectCharacters << 0 << 0 << true;
584 QTest::newRow("dog()|characters")
585 << standard[0] << 44 << 44 << QSGTextInput::SelectCharacters << 44 << 44 << true;
586 QTest::newRow("jum()ped|characters")
587 << standard[0] << 23 << 23 << QSGTextInput::SelectCharacters << 23 << 23 << true;
589 QTest::newRow("<(t)he>|words")
590 << standard[0] << 0 << 1 << QSGTextInput::SelectWords << 0 << 3 << true;
591 QTest::newRow("<do(g)>|words")
592 << standard[0] << 43 << 44 << QSGTextInput::SelectWords << 41 << 44 << true;
593 QTest::newRow("<jum(p)ed>|words")
594 << standard[0] << 23 << 24 << QSGTextInput::SelectWords << 20 << 26 << true;
595 QTest::newRow("<jumped( )>over|words,ltr")
596 << standard[0] << 26 << 27 << QSGTextInput::SelectWords << 20 << 27 << false;
597 QTest::newRow("jumped<( )over>|words,rtl")
598 << standard[0] << 27 << 26 << QSGTextInput::SelectWords << 26 << 31 << false;
599 QTest::newRow("<(the )>quick|words,ltr")
600 << standard[0] << 0 << 4 << QSGTextInput::SelectWords << 0 << 4 << false;
601 QTest::newRow("<(the )quick>|words,rtl")
602 << standard[0] << 4 << 0 << QSGTextInput::SelectWords << 0 << 9 << false;
603 QTest::newRow("<lazy( dog)>|words,ltr")
604 << standard[0] << 40 << 44 << QSGTextInput::SelectWords << 36 << 44 << false;
605 QTest::newRow("lazy<( dog)>|words,rtl")
606 << standard[0] << 44 << 40 << QSGTextInput::SelectWords << 40 << 44 << false;
607 QTest::newRow("<fox( jumped )>over|words,ltr")
608 << standard[0] << 19 << 27 << QSGTextInput::SelectWords << 16 << 27 << false;
609 QTest::newRow("fox<( jumped )over>|words,rtl")
610 << standard[0] << 27 << 19 << QSGTextInput::SelectWords << 19 << 31 << false;
611 QTest::newRow("<th(e qu)ick>|words")
612 << standard[0] << 2 << 6 << QSGTextInput::SelectWords << 0 << 9 << true;
613 QTest::newRow("<la(zy d)og|words>")
614 << standard[0] << 38 << 42 << QSGTextInput::SelectWords << 36 << 44 << true;
615 QTest::newRow("<jum(ped ov)er>|words")
616 << standard[0] << 23 << 29 << QSGTextInput::SelectWords << 20 << 31 << true;
617 QTest::newRow("<()>the|words")
618 << standard[0] << 0 << 0 << QSGTextInput::SelectWords << 0 << 0 << true;
619 QTest::newRow("dog<()>|words")
620 << standard[0] << 44 << 44 << QSGTextInput::SelectWords << 44 << 44 << true;
621 QTest::newRow("jum<()>ped|words")
622 << standard[0] << 23 << 23 << QSGTextInput::SelectWords << 23 << 23 << true;
624 QTest::newRow("Hello<(,)> |words")
625 << standard[2] << 5 << 6 << QSGTextInput::SelectWords << 5 << 6 << true;
626 QTest::newRow("Hello<(, )>world|words,ltr")
627 << standard[2] << 5 << 7 << QSGTextInput::SelectWords << 5 << 7 << false;
628 QTest::newRow("Hello<(, )world>|words,rtl")
629 << standard[2] << 7 << 5 << QSGTextInput::SelectWords << 5 << 12 << false;
630 QTest::newRow("<Hel(lo, )>world|words,ltr")
631 << standard[2] << 3 << 7 << QSGTextInput::SelectWords << 0 << 7 << false;
632 QTest::newRow("<Hel(lo, )world>|words,rtl")
633 << standard[2] << 7 << 3 << QSGTextInput::SelectWords << 0 << 12 << false;
634 QTest::newRow("<Hel(lo)>,|words")
635 << standard[2] << 3 << 5 << QSGTextInput::SelectWords << 0 << 5 << true;
636 QTest::newRow("Hello<()>,|words")
637 << standard[2] << 5 << 5 << QSGTextInput::SelectWords << 5 << 5 << true;
638 QTest::newRow("Hello,<()>|words")
639 << standard[2] << 6 << 6 << QSGTextInput::SelectWords << 6 << 6 << true;
640 QTest::newRow("Hello<,( )>world|words,ltr")
641 << standard[2] << 6 << 7 << QSGTextInput::SelectWords << 5 << 7 << false;
642 QTest::newRow("Hello,<( )world>|words,rtl")
643 << standard[2] << 7 << 6 << QSGTextInput::SelectWords << 6 << 12 << false;
644 QTest::newRow("Hello<,( world)>|words,ltr")
645 << standard[2] << 6 << 12 << QSGTextInput::SelectWords << 5 << 12 << false;
646 QTest::newRow("Hello,<( world)>|words,rtl")
647 << standard[2] << 12 << 6 << QSGTextInput::SelectWords << 6 << 12 << false;
648 QTest::newRow("Hello<,( world!)>|words,ltr")
649 << standard[2] << 6 << 13 << QSGTextInput::SelectWords << 5 << 13 << false;
650 QTest::newRow("Hello,<( world!)>|words,rtl")
651 << standard[2] << 13 << 6 << QSGTextInput::SelectWords << 6 << 13 << false;
652 QTest::newRow("Hello<(, world!)>|words")
653 << standard[2] << 5 << 13 << QSGTextInput::SelectWords << 5 << 13 << true;
654 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
656 // QTest::newRow("world<(!)>|words")
657 // << standard[2] << 12 << 13 << QSGTextInput::SelectWords << 12 << 13 << true;
658 QTest::newRow("world!<()>)|words")
659 << standard[2] << 13 << 13 << QSGTextInput::SelectWords << 13 << 13 << true;
660 QTest::newRow("world<()>!)|words")
661 << standard[2] << 12 << 12 << QSGTextInput::SelectWords << 12 << 12 << true;
663 QTest::newRow("<(,)>olleH |words")
664 << standard[3] << 7 << 8 << QSGTextInput::SelectWords << 7 << 8 << true;
665 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
666 << standard[3] << 6 << 8 << QSGTextInput::SelectWords << 1 << 8 << false;
667 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
668 << standard[3] << 8 << 6 << QSGTextInput::SelectWords << 6 << 8 << false;
669 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
670 << standard[3] << 6 << 10 << QSGTextInput::SelectWords << 1 << 13 << false;
671 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
672 << standard[3] << 10 << 6 << QSGTextInput::SelectWords << 6 << 13 << false;
673 QTest::newRow(",<(ol)leH>,|words")
674 << standard[3] << 8 << 10 << QSGTextInput::SelectWords << 8 << 13 << true;
675 QTest::newRow(",<()>olleH|words")
676 << standard[3] << 8 << 8 << QSGTextInput::SelectWords << 8 << 8 << true;
677 QTest::newRow("<()>,olleH|words")
678 << standard[3] << 7 << 7 << QSGTextInput::SelectWords << 7 << 7 << true;
679 QTest::newRow("<dlrow( )>,olleH|words,ltr")
680 << standard[3] << 6 << 7 << QSGTextInput::SelectWords << 1 << 7 << false;
681 QTest::newRow("dlrow<( ),>olleH|words,rtl")
682 << standard[3] << 7 << 6 << QSGTextInput::SelectWords << 6 << 8 << false;
683 QTest::newRow("<(dlrow )>,olleH|words,ltr")
684 << standard[3] << 1 << 7 << QSGTextInput::SelectWords << 1 << 7 << false;
685 QTest::newRow("<(dlrow ),>olleH|words,rtl")
686 << standard[3] << 7 << 1 << QSGTextInput::SelectWords << 1 << 8 << false;
687 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
688 << standard[3] << 0 << 7 << QSGTextInput::SelectWords << 0 << 7 << false;
689 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
690 << standard[3] << 7 << 0 << QSGTextInput::SelectWords << 0 << 8 << false;
691 QTest::newRow("(!dlrow ,)olleH|words")
692 << standard[3] << 0 << 8 << QSGTextInput::SelectWords << 0 << 8 << true;
693 QTest::newRow("<(!)>dlrow|words")
694 << standard[3] << 0 << 1 << QSGTextInput::SelectWords << 0 << 1 << true;
695 QTest::newRow("<()>!dlrow|words")
696 << standard[3] << 0 << 0 << QSGTextInput::SelectWords << 0 << 0 << true;
697 QTest::newRow("!<()>dlrow|words")
698 << standard[3] << 1 << 1 << QSGTextInput::SelectWords << 1 << 1 << true;
700 QTest::newRow(" <s(pac)ey> text |words")
701 << standard[4] << 1 << 4 << QSGTextInput::SelectWords << 1 << 7 << true;
702 QTest::newRow(" spacey <t(ex)t> |words")
703 << standard[4] << 11 << 13 << QSGTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
704 QTest::newRow("<( )>spacey text |words|ltr")
705 << standard[4] << 0 << 1 << QSGTextInput::SelectWords << 0 << 1 << false;
706 QTest::newRow("<( )spacey> text |words|rtl")
707 << standard[4] << 1 << 0 << QSGTextInput::SelectWords << 0 << 7 << false;
708 QTest::newRow("spacey <text( )>|words|ltr")
709 << standard[4] << 14 << 15 << QSGTextInput::SelectWords << 10 << 15 << false;
711 // QTest::newRow("spacey text<( )>|words|rtl")
712 // << standard[4] << 15 << 14 << QSGTextInput::SelectWords << 14 << 15 << false;
713 QTest::newRow("<()> spacey text |words")
714 << standard[4] << 0 << 0 << QSGTextInput::SelectWords << 0 << 0 << false;
715 QTest::newRow(" spacey text <()>|words")
716 << standard[4] << 15 << 15 << QSGTextInput::SelectWords << 15 << 15 << false;
719 void tst_qsgtextinput::moveCursorSelection()
721 QFETCH(QString, testStr);
722 QFETCH(int, cursorPosition);
723 QFETCH(int, movePosition);
724 QFETCH(QSGTextInput::SelectionMode, mode);
725 QFETCH(int, selectionStart);
726 QFETCH(int, selectionEnd);
727 QFETCH(bool, reversible);
729 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
730 QDeclarativeComponent textinputComponent(&engine);
731 textinputComponent.setData(componentStr.toLatin1(), QUrl());
732 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
733 QVERIFY(textinputObject != 0);
735 textinputObject->setCursorPosition(cursorPosition);
736 textinputObject->moveCursorSelection(movePosition, mode);
738 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
739 QCOMPARE(textinputObject->selectionStart(), selectionStart);
740 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
743 textinputObject->setCursorPosition(movePosition);
744 textinputObject->moveCursorSelection(cursorPosition, mode);
746 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
747 QCOMPARE(textinputObject->selectionStart(), selectionStart);
748 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
751 delete textinputObject;
754 void tst_qsgtextinput::moveCursorSelectionSequence_data()
756 QTest::addColumn<QString>("testStr");
757 QTest::addColumn<int>("cursorPosition");
758 QTest::addColumn<int>("movePosition1");
759 QTest::addColumn<int>("movePosition2");
760 QTest::addColumn<int>("selection1Start");
761 QTest::addColumn<int>("selection1End");
762 QTest::addColumn<int>("selection2Start");
763 QTest::addColumn<int>("selection2End");
765 // () contains the text selected by the cursor.
766 // <> contains the actual selection.
767 // ^ is the revised cursor position.
768 // {} contains the revised selection.
770 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
775 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
780 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
785 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
790 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
795 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
800 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
805 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
810 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
815 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
820 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
825 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
830 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
835 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
840 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
845 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
850 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
855 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
861 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
866 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
872 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
877 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
883 QTest::newRow("{<(^} sp)acey> text |ltr")
888 QTest::newRow("{<( ^}sp)acey> text |ltr")
893 QTest::newRow("<( {s^p)acey>} text |rtl")
898 QTest::newRow("<( {^sp)acey>} text |rtl")
904 QTest::newRow(" spacey <te(xt {^)>}|rtl")
910 // QTest::newRow(" spacey <te(xt{^ )>}|rtl")
915 QTest::newRow(" spacey {<te(x^t} )>|ltr")
921 // QTest::newRow(" spacey {<te(xt^} )>|ltr")
928 void tst_qsgtextinput::moveCursorSelectionSequence()
930 QFETCH(QString, testStr);
931 QFETCH(int, cursorPosition);
932 QFETCH(int, movePosition1);
933 QFETCH(int, movePosition2);
934 QFETCH(int, selection1Start);
935 QFETCH(int, selection1End);
936 QFETCH(int, selection2Start);
937 QFETCH(int, selection2End);
939 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
940 QDeclarativeComponent textinputComponent(&engine);
941 textinputComponent.setData(componentStr.toLatin1(), QUrl());
942 QSGTextInput *textinputObject = qobject_cast<QSGTextInput*>(textinputComponent.create());
943 QVERIFY(textinputObject != 0);
945 textinputObject->setCursorPosition(cursorPosition);
947 textinputObject->moveCursorSelection(movePosition1, QSGTextInput::SelectWords);
948 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
949 QCOMPARE(textinputObject->selectionStart(), selection1Start);
950 QCOMPARE(textinputObject->selectionEnd(), selection1End);
952 textinputObject->moveCursorSelection(movePosition2, QSGTextInput::SelectWords);
953 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
954 QCOMPARE(textinputObject->selectionStart(), selection2Start);
955 QCOMPARE(textinputObject->selectionEnd(), selection2End);
957 delete textinputObject;
960 void tst_qsgtextinput::dragMouseSelection()
962 QString qmlfile = SRCDIR "/data/mouseselection_true.qml";
964 QSGView canvas(QUrl::fromLocalFile(qmlfile));
967 QApplication::setActiveWindow(&canvas);
968 QTest::qWaitForWindowShown(&canvas);
969 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
971 QVERIFY(canvas.rootObject() != 0);
972 QSGTextInput *textInputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
973 QVERIFY(textInputObject != 0);
975 // press-and-drag-and-release from x1 to x2
978 int y = textInputObject->height()/2;
979 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
981 QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
982 QApplication::sendEvent(&canvas, &mv);
984 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
986 QString str1 = textInputObject->selectedText();
987 QVERIFY(str1.length() > 3);
989 // press and drag the current selection.
992 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
994 QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
995 QApplication::sendEvent(&canvas, &mv);
997 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
998 QString str2 = textInputObject->selectedText();
999 QVERIFY(str2.length() > 3);
1001 QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and doesn't not the first moved.
1004 void tst_qsgtextinput::mouseSelectionMode_data()
1006 QTest::addColumn<QString>("qmlfile");
1007 QTest::addColumn<bool>("selectWords");
1010 QTest::newRow("SelectWords") << SRCDIR "/data/mouseselectionmode_words.qml" << true;
1011 QTest::newRow("SelectCharacters") << SRCDIR "/data/mouseselectionmode_characters.qml" << false;
1012 QTest::newRow("default") << SRCDIR "/data/mouseselectionmode_default.qml" << false;
1015 void tst_qsgtextinput::mouseSelectionMode()
1017 QFETCH(QString, qmlfile);
1018 QFETCH(bool, selectWords);
1020 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1022 QSGView canvas(QUrl::fromLocalFile(qmlfile));
1025 QApplication::setActiveWindow(&canvas);
1026 QTest::qWaitForWindowShown(&canvas);
1027 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
1029 QVERIFY(canvas.rootObject() != 0);
1030 QSGTextInput *textInputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
1031 QVERIFY(textInputObject != 0);
1033 // press-and-drag-and-release from x1 to x2
1036 int y = textInputObject->height()/2;
1037 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1038 //QTest::mouseMove(&canvas, canvas.mapFromScene(QPoint(x2,y))); // doesn't work
1039 QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1040 QApplication::sendEvent(&canvas, &mv);
1041 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1042 QString str = textInputObject->selectedText();
1044 QCOMPARE(str, text);
1046 QVERIFY(str.length() > 3);
1047 QVERIFY(str != text);
1051 void tst_qsgtextinput::horizontalAlignment_data()
1053 QTest::addColumn<int>("hAlign");
1054 QTest::addColumn<QString>("expectfile");
1056 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1057 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1058 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1061 void tst_qsgtextinput::horizontalAlignment()
1063 QFETCH(int, hAlign);
1064 QFETCH(QString, expectfile);
1066 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/horizontalAlignment.qml"));
1069 QApplication::setActiveWindow(&canvas);
1070 QTest::qWaitForWindowShown(&canvas);
1071 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
1072 QObject *ob = canvas.rootObject();
1074 ob->setProperty("horizontalAlignment",hAlign);
1075 QImage actual = canvas.grabFrameBuffer();
1077 expectfile = createExpectedFileIfNotFound(expectfile, actual);
1079 QImage expect(expectfile);
1081 QCOMPARE(actual,expect);
1084 void tst_qsgtextinput::horizontalAlignment_RightToLeft()
1086 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/horizontalAlignment_RightToLeft.qml"));
1087 QSGTextInput *textInput = canvas.rootObject()->findChild<QSGTextInput*>("text");
1088 QVERIFY(textInput != 0);
1091 const QString rtlText = textInput->text();
1093 QSGTextInputPrivate *textInputPrivate = QSGTextInputPrivate::get(textInput);
1094 QVERIFY(textInputPrivate != 0);
1095 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1097 // implicit alignment should follow the reading direction of RTL text
1098 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1099 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1100 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1102 // explicitly left aligned
1103 textInput->setHAlign(QSGTextInput::AlignLeft);
1104 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
1105 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1106 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1108 // explicitly right aligned
1109 textInput->setHAlign(QSGTextInput::AlignRight);
1110 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1111 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1112 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1114 // explicitly center aligned
1115 textInput->setHAlign(QSGTextInput::AlignHCenter);
1116 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1117 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignHCenter);
1118 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1119 QVERIFY(-textInputPrivate->hscroll + textInputPrivate->width > canvas.width()/2);
1121 // reseted alignment should go back to following the text reading direction
1122 textInput->resetHAlign();
1123 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1124 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1125 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1127 // mirror the text item
1128 QSGItemPrivate::get(textInput)->setLayoutMirror(true);
1130 // mirrored implicit alignment should continue to follow the reading direction of the text
1131 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1132 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1133 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1135 // explicitly right aligned behaves as left aligned
1136 textInput->setHAlign(QSGTextInput::AlignRight);
1137 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1138 QCOMPARE(textInput->effectiveHAlign(), QSGTextInput::AlignLeft);
1139 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1141 // mirrored explicitly left aligned behaves as right aligned
1142 textInput->setHAlign(QSGTextInput::AlignLeft);
1143 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
1144 QCOMPARE(textInput->effectiveHAlign(), QSGTextInput::AlignRight);
1145 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1147 // disable mirroring
1148 QSGItemPrivate::get(textInput)->setLayoutMirror(false);
1149 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1150 textInput->resetHAlign();
1152 // English text should be implicitly left aligned
1153 textInput->setText("Hello world!");
1154 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
1155 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1157 QApplication::setActiveWindow(&canvas);
1158 QTest::qWaitForWindowShown(&canvas);
1159 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
1161 // If there is no commited text, the preedit text should determine the alignment.
1162 textInput->setText(QString());
1163 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(&canvas, &ev); }
1164 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1165 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(&canvas, &ev); }
1166 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft);
1168 #ifndef Q_OS_MAC // QTBUG-18040
1169 // empty text with implicit alignment follows the system locale-based
1170 // keyboard input direction from QApplication::keyboardInputDirection
1171 textInput->setText("");
1172 QCOMPARE(textInput->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
1173 QSGTextInput::AlignLeft : QSGTextInput::AlignRight);
1174 if (QApplication::keyboardInputDirection() == Qt::LeftToRight)
1175 QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1177 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1178 textInput->setHAlign(QSGTextInput::AlignRight);
1179 QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight);
1180 QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1183 #ifndef Q_OS_MAC // QTBUG-18040
1184 // alignment of TextInput with no text set to it
1185 QString componentStr = "import QtQuick 2.0\nTextInput {}";
1186 QDeclarativeComponent textComponent(&engine);
1187 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1188 QSGTextInput *textObject = qobject_cast<QSGTextInput*>(textComponent.create());
1189 QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
1190 QSGTextInput::AlignLeft : QSGTextInput::AlignRight);
1195 void tst_qsgtextinput::positionAt()
1197 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/positionAt.qml"));
1198 QVERIFY(canvas.rootObject() != 0);
1201 QApplication::setActiveWindow(&canvas);
1202 QTest::qWaitForWindowShown(&canvas);
1204 QSGTextInput *textinputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
1205 QVERIFY(textinputObject != 0);
1207 // Check autoscrolled...
1208 QFontMetrics fm(textinputObject->font());
1210 int pos = textinputObject->positionAt(textinputObject->width()/2);
1212 int textLeftWidth = 0;
1213 if (!qmlDisableDistanceField()) {
1215 QTextLayout layout(textinputObject->text().left(pos));
1219 option.setUseDesignMetrics(true);
1220 layout.setTextOption(option);
1223 layout.beginLayout();
1224 QTextLine line = layout.createLine();
1227 textLeftWidth = ceil(line.horizontalAdvance());
1230 QTextLayout layout(textinputObject->text());
1234 option.setUseDesignMetrics(true);
1235 layout.setTextOption(option);
1238 layout.beginLayout();
1239 QTextLine line = layout.createLine();
1242 textWidth = ceil(line.horizontalAdvance());
1245 textWidth = fm.width(textinputObject->text());
1246 textLeftWidth = fm.width(textinputObject->text().left(pos));
1249 int diff = abs(textWidth - (textLeftWidth+textinputObject->width()/2));
1252 QEXPECT_FAIL("", "QTBUG-21011 fails", Continue);
1255 // some tollerance for different fonts.
1262 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1263 QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorBetweenCharacters), pos + 1);
1264 QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorOnCharacter), pos);
1266 // Check without autoscroll...
1267 textinputObject->setAutoScroll(false);
1268 pos = textinputObject->positionAt(textinputObject->width()/2);
1270 if (!qmlDisableDistanceField()) {
1272 QTextLayout layout(textinputObject->text().left(pos));
1276 option.setUseDesignMetrics(true);
1277 layout.setTextOption(option);
1280 layout.beginLayout();
1281 QTextLine line = layout.createLine();
1284 textLeftWidth = ceil(line.horizontalAdvance());
1287 textLeftWidth = fm.width(textinputObject->text().left(pos));
1290 diff = abs(int(textLeftWidth-textinputObject->width()/2));
1293 QEXPECT_FAIL("", "QTBUG-21011 fails", Continue);
1296 // some tollerance for different fonts.
1303 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1304 QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorBetweenCharacters), pos + 1);
1305 QCOMPARE(textinputObject->positionAt(x, QSGTextInput::CursorOnCharacter), pos);
1307 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1308 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1310 QString preeditText = textinputObject->text().mid(0, pos);
1311 textinputObject->setText(textinputObject->text().mid(pos));
1312 textinputObject->setCursorPosition(0);
1314 QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1315 QApplication::sendEvent(&canvas, &inputEvent);
1317 // Check all points within the preedit text return the same position.
1318 QCOMPARE(textinputObject->positionAt(0), 0);
1319 QCOMPARE(textinputObject->positionAt(x0 / 2), 0);
1320 QCOMPARE(textinputObject->positionAt(x0), 0);
1322 // Verify positioning returns to normal after the preedit text.
1323 QCOMPARE(textinputObject->positionAt(x1), 1);
1324 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1327 void tst_qsgtextinput::maxLength()
1329 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/maxLength.qml"));
1330 QVERIFY(canvas.rootObject() != 0);
1333 QApplication::setActiveWindow(&canvas);
1334 QTest::qWaitForWindowShown(&canvas);
1336 QSGTextInput *textinputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
1337 QVERIFY(textinputObject != 0);
1338 QVERIFY(textinputObject->text().isEmpty());
1339 QVERIFY(textinputObject->maxLength() == 10);
1340 foreach(const QString &str, standard){
1341 QVERIFY(textinputObject->text().length() <= 10);
1342 textinputObject->setText(str);
1343 QVERIFY(textinputObject->text().length() <= 10);
1346 textinputObject->setText("");
1347 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1348 for(int i=0; i<20; i++){
1349 QCOMPARE(textinputObject->text().length(), qMin(i,10));
1350 //simulateKey(&canvas, Qt::Key_A);
1351 QTest::keyPress(&canvas, Qt::Key_A);
1352 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1356 void tst_qsgtextinput::masks()
1358 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1359 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1360 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/masks.qml"));
1363 QVERIFY(canvas.rootObject() != 0);
1364 QSGTextInput *textinputObject = qobject_cast<QSGTextInput *>(canvas.rootObject());
1365 QVERIFY(textinputObject != 0);
1366 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1367 QVERIFY(textinputObject->text().length() == 0);
1368 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1369 for(int i=0; i<10; i++){
1370 QCOMPARE(qMin(i,8), textinputObject->text().length());
1371 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1372 //simulateKey(&canvas, Qt::Key_A);
1373 QTest::keyPress(&canvas, Qt::Key_A);
1374 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1378 void tst_qsgtextinput::validators()
1380 // Note that this test assumes that the validators are working properly
1381 // so you may need to run their tests first. All validators are checked
1382 // here to ensure that their exposure to QML is working.
1384 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/validators.qml"));
1388 QVERIFY(canvas.rootObject() != 0);
1390 QSGTextInput *intInput = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1392 intInput->setFocus(true);
1393 QTRY_VERIFY(intInput->hasActiveFocus());
1394 QTest::keyPress(&canvas, Qt::Key_1);
1395 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1396 QCOMPARE(intInput->text(), QLatin1String("1"));
1397 QCOMPARE(intInput->hasAcceptableInput(), false);
1398 QTest::keyPress(&canvas, Qt::Key_2);
1399 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1400 QCOMPARE(intInput->text(), QLatin1String("1"));
1401 QCOMPARE(intInput->hasAcceptableInput(), false);
1402 QTest::keyPress(&canvas, Qt::Key_1);
1403 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1404 QCOMPARE(intInput->text(), QLatin1String("11"));
1405 QCOMPARE(intInput->hasAcceptableInput(), true);
1406 QTest::keyPress(&canvas, Qt::Key_0);
1407 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1408 QCOMPARE(intInput->text(), QLatin1String("11"));
1409 QCOMPARE(intInput->hasAcceptableInput(), true);
1411 QSGTextInput *dblInput = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1412 QTRY_VERIFY(dblInput);
1413 dblInput->setFocus(true);
1414 QVERIFY(dblInput->hasActiveFocus() == true);
1415 QTest::keyPress(&canvas, Qt::Key_1);
1416 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1417 QCOMPARE(dblInput->text(), QLatin1String("1"));
1418 QCOMPARE(dblInput->hasAcceptableInput(), false);
1419 QTest::keyPress(&canvas, Qt::Key_2);
1420 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1421 QCOMPARE(dblInput->text(), QLatin1String("12"));
1422 QCOMPARE(dblInput->hasAcceptableInput(), true);
1423 QTest::keyPress(&canvas, Qt::Key_Period);
1424 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1425 QCOMPARE(dblInput->text(), QLatin1String("12."));
1426 QCOMPARE(dblInput->hasAcceptableInput(), true);
1427 QTest::keyPress(&canvas, Qt::Key_1);
1428 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1429 QCOMPARE(dblInput->text(), QLatin1String("12.1"));
1430 QCOMPARE(dblInput->hasAcceptableInput(), true);
1431 QTest::keyPress(&canvas, Qt::Key_1);
1432 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1433 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1434 QCOMPARE(dblInput->hasAcceptableInput(), true);
1435 QTest::keyPress(&canvas, Qt::Key_1);
1436 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1437 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1438 QCOMPARE(dblInput->hasAcceptableInput(), true);
1440 QSGTextInput *strInput = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1441 QTRY_VERIFY(strInput);
1442 strInput->setFocus(true);
1443 QVERIFY(strInput->hasActiveFocus() == true);
1444 QTest::keyPress(&canvas, Qt::Key_1);
1445 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1446 QCOMPARE(strInput->text(), QLatin1String(""));
1447 QCOMPARE(strInput->hasAcceptableInput(), false);
1448 QTest::keyPress(&canvas, Qt::Key_A);
1449 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1450 QCOMPARE(strInput->text(), QLatin1String("a"));
1451 QCOMPARE(strInput->hasAcceptableInput(), false);
1452 QTest::keyPress(&canvas, Qt::Key_A);
1453 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1454 QCOMPARE(strInput->text(), QLatin1String("aa"));
1455 QCOMPARE(strInput->hasAcceptableInput(), true);
1456 QTest::keyPress(&canvas, Qt::Key_A);
1457 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1458 QCOMPARE(strInput->text(), QLatin1String("aaa"));
1459 QCOMPARE(strInput->hasAcceptableInput(), true);
1460 QTest::keyPress(&canvas, Qt::Key_A);
1461 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1462 QCOMPARE(strInput->text(), QLatin1String("aaaa"));
1463 QCOMPARE(strInput->hasAcceptableInput(), true);
1464 QTest::keyPress(&canvas, Qt::Key_A);
1465 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1466 QCOMPARE(strInput->text(), QLatin1String("aaaa"));
1467 QCOMPARE(strInput->hasAcceptableInput(), true);
1470 void tst_qsgtextinput::inputMethods()
1472 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/inputmethods.qml"));
1475 QApplication::setActiveWindow(&canvas);
1476 QTest::qWaitForWindowShown(&canvas);
1478 // test input method hints
1479 QVERIFY(canvas.rootObject() != 0);
1480 QSGTextInput *input = qobject_cast<QSGTextInput *>(canvas.rootObject());
1481 QVERIFY(input != 0);
1482 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1483 input->setInputMethodHints(Qt::ImhUppercaseOnly);
1484 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1486 input->setFocus(true);
1487 QVERIFY(input->hasActiveFocus() == true);
1488 // test that input method event is committed
1489 QInputMethodEvent event;
1490 event.setCommitString( "My ", -12, 0);
1491 QApplication::sendEvent(&canvas, &event);
1492 QCOMPARE(input->text(), QString("My Hello world!"));
1494 input->setCursorPosition(2);
1495 event.setCommitString("Your", -2, 2);
1496 QApplication::sendEvent(&canvas, &event);
1497 QCOMPARE(input->text(), QString("Your Hello world!"));
1498 QCOMPARE(input->cursorPosition(), 4);
1500 input->setCursorPosition(7);
1501 event.setCommitString("Goodbye", -2, 5);
1502 QApplication::sendEvent(&canvas, &event);
1503 QCOMPARE(input->text(), QString("Your Goodbye world!"));
1504 QCOMPARE(input->cursorPosition(), 12);
1506 input->setCursorPosition(8);
1507 event.setCommitString("Our", -8, 4);
1508 QApplication::sendEvent(&canvas, &event);
1509 QCOMPARE(input->text(), QString("Our Goodbye world!"));
1510 QCOMPARE(input->cursorPosition(), 7);
1514 TextInput element should only handle left/right keys until the cursor reaches
1515 the extent of the text, then they should ignore the keys.
1518 void tst_qsgtextinput::navigation()
1520 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/navigation.qml"));
1524 QVERIFY(canvas.rootObject() != 0);
1526 QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1528 QVERIFY(input != 0);
1529 input->setCursorPosition(0);
1530 QTRY_VERIFY(input->hasActiveFocus() == true);
1531 simulateKey(&canvas, Qt::Key_Left);
1532 QVERIFY(input->hasActiveFocus() == false);
1533 simulateKey(&canvas, Qt::Key_Right);
1534 QVERIFY(input->hasActiveFocus() == true);
1535 //QT-2944: If text is selected, ensure we deselect upon cursor motion
1536 input->setCursorPosition(input->text().length());
1537 input->select(0,input->text().length());
1538 QVERIFY(input->selectionStart() != input->selectionEnd());
1539 simulateKey(&canvas, Qt::Key_Right);
1540 QVERIFY(input->selectionStart() == input->selectionEnd());
1541 QVERIFY(input->selectionStart() == input->text().length());
1542 QVERIFY(input->hasActiveFocus() == true);
1543 simulateKey(&canvas, Qt::Key_Right);
1544 QVERIFY(input->hasActiveFocus() == false);
1545 simulateKey(&canvas, Qt::Key_Left);
1546 QVERIFY(input->hasActiveFocus() == true);
1548 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1549 input->setCursorPosition(2);
1550 QCOMPARE(input->cursorPosition(),2);
1551 simulateKey(&canvas, Qt::Key_Up);
1552 QCOMPARE(input->cursorPosition(),2);
1553 simulateKey(&canvas, Qt::Key_Down);
1554 QCOMPARE(input->cursorPosition(),2);
1557 void tst_qsgtextinput::navigation_RTL()
1559 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/navigation.qml"));
1563 QVERIFY(canvas.rootObject() != 0);
1565 QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1567 QVERIFY(input != 0);
1568 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1569 input->setText(QString::fromUtf16(arabic_str, 11));
1571 input->setCursorPosition(0);
1572 QTRY_VERIFY(input->hasActiveFocus() == true);
1575 simulateKey(&canvas, Qt::Key_Right);
1576 QVERIFY(input->hasActiveFocus() == false);
1579 simulateKey(&canvas, Qt::Key_Left);
1580 QVERIFY(input->hasActiveFocus() == true);
1582 input->setCursorPosition(input->text().length());
1583 QVERIFY(input->hasActiveFocus() == true);
1586 simulateKey(&canvas, Qt::Key_Left);
1587 QVERIFY(input->hasActiveFocus() == false);
1590 simulateKey(&canvas, Qt::Key_Right);
1591 QVERIFY(input->hasActiveFocus() == true);
1594 void tst_qsgtextinput::copyAndPaste() {
1595 #ifndef QT_NO_CLIPBOARD
1599 PasteboardRef pasteboard;
1600 OSStatus status = PasteboardCreate(0, &pasteboard);
1601 if (status == noErr)
1602 CFRelease(pasteboard);
1604 QSKIP("This machine doesn't support the clipboard", SkipAll);
1608 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1609 QDeclarativeComponent textInputComponent(&engine);
1610 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1611 QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
1612 QVERIFY(textInput != 0);
1615 QCOMPARE(textInput->text().length(), 12);
1616 textInput->select(0, textInput->text().length());;
1618 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1619 QCOMPARE(textInput->selectedText().length(), 12);
1620 textInput->setCursorPosition(0);
1621 QVERIFY(textInput->canPaste());
1623 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1624 QCOMPARE(textInput->text().length(), 24);
1627 QVERIFY(textInput->canPaste());
1628 textInput->setReadOnly(true);
1629 QVERIFY(!textInput->canPaste());
1630 textInput->setReadOnly(false);
1631 QVERIFY(textInput->canPaste());
1634 textInput->setCursorPosition(0);
1635 textInput->selectWord();
1636 QCOMPARE(textInput->selectedText(), QString("Hello"));
1638 // select all and cut
1639 textInput->selectAll();
1641 QCOMPARE(textInput->text().length(), 0);
1643 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1644 QCOMPARE(textInput->text().length(), 24);
1646 // clear copy buffer
1647 QClipboard *clipboard = QApplication::clipboard();
1650 QVERIFY(!textInput->canPaste());
1652 // test that copy functionality is disabled
1653 // when echo mode is set to hide text/password mode
1656 QSGTextInput::EchoMode echoMode = QSGTextInput::EchoMode(index);
1657 textInput->setEchoMode(echoMode);
1658 textInput->setText("My password");
1659 textInput->select(0, textInput->text().length());;
1661 if (echoMode == QSGTextInput::Normal) {
1662 QVERIFY(!clipboard->text().isEmpty());
1663 QCOMPARE(clipboard->text(), QString("My password"));
1666 QVERIFY(clipboard->text().isEmpty());
1675 void tst_qsgtextinput::canPasteEmpty() {
1676 #ifndef QT_NO_CLIPBOARD
1678 QApplication::clipboard()->clear();
1680 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1681 QDeclarativeComponent textInputComponent(&engine);
1682 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1683 QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
1684 QVERIFY(textInput != 0);
1687 bool cp = !lc.isReadOnly() && QApplication::clipboard()->text().length() != 0;
1688 QCOMPARE(textInput->canPaste(), cp);
1693 void tst_qsgtextinput::canPaste() {
1694 #ifndef QT_NO_CLIPBOARD
1696 QApplication::clipboard()->setText("Some text");
1698 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1699 QDeclarativeComponent textInputComponent(&engine);
1700 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1701 QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
1702 QVERIFY(textInput != 0);
1705 bool cp = !lc.isReadOnly() && QApplication::clipboard()->text().length() != 0;
1706 QCOMPARE(textInput->canPaste(), cp);
1711 void tst_qsgtextinput::passwordCharacter()
1713 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
1714 QDeclarativeComponent textInputComponent(&engine);
1715 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1716 QSGTextInput *textInput = qobject_cast<QSGTextInput*>(textInputComponent.create());
1717 QVERIFY(textInput != 0);
1719 textInput->setPasswordCharacter("X");
1720 qreal implicitWidth = textInput->implicitWidth();
1721 textInput->setPasswordCharacter(".");
1723 // QTBUG-12383 content is updated and redrawn
1724 QVERIFY(textInput->implicitWidth() < implicitWidth);
1729 void tst_qsgtextinput::cursorDelegate()
1731 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/cursorTest.qml"));
1734 QSGTextInput *textInputObject = view.rootObject()->findChild<QSGTextInput*>("textInputObject");
1735 QVERIFY(textInputObject != 0);
1736 QVERIFY(textInputObject->findChild<QSGItem*>("cursorInstance"));
1737 //Test Delegate gets created
1738 textInputObject->setFocus(true);
1739 QSGItem* delegateObject = textInputObject->findChild<QSGItem*>("cursorInstance");
1740 QVERIFY(delegateObject);
1741 //Test Delegate gets moved
1742 for(int i=0; i<= textInputObject->text().length(); i++){
1743 textInputObject->setCursorPosition(i);
1744 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1745 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1747 textInputObject->setCursorPosition(0);
1748 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1749 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1750 //Test Delegate gets deleted
1751 textInputObject->setCursorDelegate(0);
1752 QVERIFY(!textInputObject->findChild<QSGItem*>("cursorInstance"));
1755 void tst_qsgtextinput::cursorVisible()
1757 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/cursorVisible.qml"));
1759 QApplication::setActiveWindow(&view);
1760 QTest::qWaitForWindowShown(&view);
1761 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
1765 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
1767 QCOMPARE(input.isCursorVisible(), false);
1769 input.setCursorVisible(true);
1770 QCOMPARE(input.isCursorVisible(), true);
1771 QCOMPARE(spy.count(), 1);
1773 input.setCursorVisible(false);
1774 QCOMPARE(input.isCursorVisible(), false);
1775 QCOMPARE(spy.count(), 2);
1777 input.setFocus(true);
1778 QCOMPARE(input.isCursorVisible(), false);
1779 QCOMPARE(spy.count(), 2);
1781 input.setParentItem(view.rootObject());
1782 QCOMPARE(input.isCursorVisible(), true);
1783 QCOMPARE(spy.count(), 3);
1785 input.setFocus(false);
1786 QCOMPARE(input.isCursorVisible(), false);
1787 QCOMPARE(spy.count(), 4);
1789 input.setFocus(true);
1790 QCOMPARE(input.isCursorVisible(), true);
1791 QCOMPARE(spy.count(), 5);
1794 QCOMPARE(input.isCursorVisible(), false);
1795 QCOMPARE(spy.count(), 6);
1798 QCOMPARE(input.isCursorVisible(), true);
1799 QCOMPARE(spy.count(), 7);
1801 // on mac, setActiveWindow(0) on mac does not deactivate the current application
1802 // (you have to switch to a different app or hide the current app to trigger this)
1803 #if !defined(Q_WS_MAC)
1804 QApplication::setActiveWindow(0);
1805 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(0));
1806 QCOMPARE(input.isCursorVisible(), false);
1807 QCOMPARE(spy.count(), 8);
1809 QApplication::setActiveWindow(&view);
1810 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
1811 QCOMPARE(input.isCursorVisible(), true);
1812 QCOMPARE(spy.count(), 9);
1816 void tst_qsgtextinput::cursorRectangle()
1818 QString text = "Hello World!";
1821 input.setText(text);
1822 QFontMetricsF fm(input.font());
1823 input.setWidth(fm.width(text.mid(0, 5)));
1827 // some tolerance for different fonts.
1829 const int error = 2;
1831 const int error = 5;
1835 for (int i = 0; i <= 5; ++i) {
1836 input.setCursorPosition(i);
1837 r = input.cursorRectangle();
1838 int textWidth = fm.width(text.mid(0, i));
1840 QVERIFY(r.left() < textWidth + error);
1841 QVERIFY(r.right() > textWidth - error);
1842 QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
1845 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
1846 QVERIFY(r.left() < input.boundingRect().width());
1847 QVERIFY(r.right() >= input.width() - error);
1849 for (int i = 6; i < text.length(); ++i) {
1850 input.setCursorPosition(i);
1851 QCOMPARE(r, input.cursorRectangle());
1852 QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
1855 for (int i = text.length() - 2; i >= 0; --i) {
1856 input.setCursorPosition(i);
1857 r = input.cursorRectangle();
1858 QVERIFY(r.right() >= 0);
1859 QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
1862 input.setText("Hi!");
1863 input.setHAlign(QSGTextInput::AlignRight);
1864 r = input.cursorRectangle();
1865 QVERIFY(r.left() < input.boundingRect().width());
1866 QVERIFY(r.right() >= input.width() - error);
1869 void tst_qsgtextinput::readOnly()
1871 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/readOnly.qml"));
1875 QVERIFY(canvas.rootObject() != 0);
1877 QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1879 QVERIFY(input != 0);
1880 QTRY_VERIFY(input->hasActiveFocus() == true);
1881 QVERIFY(input->isReadOnly() == true);
1882 QString initial = input->text();
1883 for(int k=Qt::Key_0; k<=Qt::Key_Z; k++)
1884 simulateKey(&canvas, k);
1885 simulateKey(&canvas, Qt::Key_Return);
1886 simulateKey(&canvas, Qt::Key_Space);
1887 simulateKey(&canvas, Qt::Key_Escape);
1888 QCOMPARE(input->text(), initial);
1890 input->setCursorPosition(3);
1891 input->setReadOnly(false);
1892 QCOMPARE(input->isReadOnly(), false);
1893 QCOMPARE(input->cursorPosition(), input->text().length());
1896 void tst_qsgtextinput::echoMode()
1898 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/echoMode.qml"));
1901 QApplication::setActiveWindow(&canvas);
1902 QTest::qWaitForWindowShown(&canvas);
1903 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
1905 QVERIFY(canvas.rootObject() != 0);
1907 QSGTextInput *input = qobject_cast<QSGTextInput *>(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(), QSGTextInput::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(QSGTextInput::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(QSGTextInput::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(QSGTextInput::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 QInputMethodEvent inputEvent;
1958 inputEvent.setCommitString(initial);
1959 QApplication::sendEvent(&canvas, &inputEvent);
1960 QCOMPARE(input->text(), initial);
1961 QCOMPARE(input->displayText(), initial);
1962 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
1965 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
1966 void tst_qdeclarativetextinput::passwordEchoDelay()
1968 QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/echoMode.qml"));
1971 QApplication::setActiveWindow(&canvas);
1972 QTest::qWaitForWindowShown(&canvas);
1973 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas));
1975 QVERIFY(canvas.rootObject() != 0);
1977 QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1979 QChar fillChar = QLatin1Char('*');
1981 input->setEchoMode(QDeclarativeTextInput::Password);
1982 QCOMPARE(input->displayText(), QString(8, fillChar));
1983 input->setText(QString());
1984 QCOMPARE(input->displayText(), QString());
1986 QTest::keyPress(&canvas, '0');
1987 QTest::keyPress(&canvas, '1');
1988 QTest::keyPress(&canvas, '2');
1989 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
1990 QTest::keyPress(&canvas, '3');
1991 QTest::keyPress(&canvas, '4');
1992 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1993 QTest::keyPress(&canvas, Qt::Key_Backspace);
1994 QCOMPARE(input->displayText(), QString(4, fillChar));
1995 QTest::keyPress(&canvas, '4');
1996 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1997 QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
1998 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
1999 QTest::keyPress(&canvas, '5');
2000 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2001 input->setFocus(false);
2002 QVERIFY(!input->hasFocus());
2003 QCOMPARE(input->displayText(), QString(6, fillChar));
2004 input->setFocus(true);
2005 QTRY_VERIFY(input->hasFocus());
2006 QCOMPARE(input->displayText(), QString(6, fillChar));
2007 QTest::keyPress(&canvas, '6');
2008 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2010 QInputMethodEvent ev;
2011 ev.setCommitString(QLatin1String("7"));
2012 QApplication::sendEvent(&canvas, &ev);
2013 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2018 void tst_qsgtextinput::simulateKey(QSGView *view, int key)
2020 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2021 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2023 QApplication::sendEvent(view, &press);
2024 QApplication::sendEvent(view, &release);
2027 class MyInputContext : public QInputContext
2030 MyInputContext() : openInputPanelReceived(false), closeInputPanelReceived(false), updateReceived(false), eventType(QEvent::None) {}
2031 ~MyInputContext() {}
2033 QString identifierName() { return QString(); }
2034 QString language() { return QString(); }
2038 bool isComposing() const { return false; }
2040 bool filterEvent( const QEvent *event )
2042 if (event->type() == QEvent::RequestSoftwareInputPanel)
2043 openInputPanelReceived = true;
2044 if (event->type() == QEvent::CloseSoftwareInputPanel)
2045 closeInputPanelReceived = true;
2046 return QInputContext::filterEvent(event);
2049 void update() { updateReceived = true; }
2051 void mouseHandler(int x, QMouseEvent *event)
2054 eventType = event->type();
2055 eventPosition = event->pos();
2056 eventGlobalPosition = event->globalPos();
2057 eventButton = event->button();
2058 eventButtons = event->buttons();
2059 eventModifiers = event->modifiers();
2062 void sendPreeditText(const QString &text, int cursor)
2064 QList<QInputMethodEvent::Attribute> attributes;
2065 attributes.append(QInputMethodEvent::Attribute(
2066 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2068 QInputMethodEvent event(text, attributes);
2072 bool openInputPanelReceived;
2073 bool closeInputPanelReceived;
2074 bool updateReceived;
2076 QEvent::Type eventType;
2077 QPoint eventPosition;
2078 QPoint eventGlobalPosition;
2079 Qt::MouseButton eventButton;
2080 Qt::MouseButtons eventButtons;
2081 Qt::KeyboardModifiers eventModifiers;
2084 void tst_qsgtextinput::openInputPanelOnClick()
2086 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/openInputPanel.qml"));
2088 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
2089 // and QWidget won't allow an input context to be set when the flag is not set.
2090 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2091 view.setInputContext(&ic);
2092 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2094 QApplication::setActiveWindow(&view);
2095 QTest::qWaitForWindowShown(&view);
2096 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2097 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2100 QSGItemPrivate* pri = QSGItemPrivate::get(input);
2101 QSGTextInputPrivate *inputPrivate = static_cast<QSGTextInputPrivate*>(pri);
2103 // input panel on click
2104 inputPrivate->showInputPanelOnFocus = false;
2106 QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(
2107 view.style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
2108 QTest::mouseClick(&view, Qt::LeftButton, 0, input->pos().toPoint());
2109 QApplication::processEvents();
2110 if (behavior == QStyle::RSIP_OnMouseClickAndAlreadyFocused) {
2111 QCOMPARE(ic.openInputPanelReceived, false);
2112 QTest::mouseClick(&view, Qt::LeftButton, 0, input->pos().toPoint());
2113 QApplication::processEvents();
2114 QCOMPARE(ic.openInputPanelReceived, true);
2115 } else if (behavior == QStyle::RSIP_OnMouseClick) {
2116 QCOMPARE(ic.openInputPanelReceived, true);
2118 ic.openInputPanelReceived = false;
2120 // focus should not cause input panels to open or close
2121 input->setFocus(false);
2122 input->setFocus(true);
2123 input->setFocus(false);
2124 input->setFocus(true);
2125 input->setFocus(false);
2126 QCOMPARE(ic.openInputPanelReceived, false);
2127 QCOMPARE(ic.closeInputPanelReceived, false);
2130 void tst_qsgtextinput::openInputPanelOnFocus()
2132 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/openInputPanel.qml"));
2134 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
2135 // and QWidget won't allow an input context to be set when the flag is not set.
2136 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2137 view.setInputContext(&ic);
2138 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2140 QApplication::setActiveWindow(&view);
2141 QTest::qWaitForWindowShown(&view);
2142 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2143 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2145 QSignalSpy focusOnPressSpy(input, SIGNAL(activeFocusOnPressChanged(bool)));
2147 QSGItemPrivate* pri = QSGItemPrivate::get(input);
2148 QSGTextInputPrivate *inputPrivate = static_cast<QSGTextInputPrivate*>(pri);
2149 inputPrivate->showInputPanelOnFocus = true;
2151 // test default values
2152 QVERIFY(input->focusOnPress());
2153 QCOMPARE(ic.openInputPanelReceived, false);
2154 QCOMPARE(ic.closeInputPanelReceived, false);
2156 // focus on press, input panel on focus
2157 QTest::mousePress(&view, Qt::LeftButton, 0, input->pos().toPoint());
2158 QApplication::processEvents();
2159 QVERIFY(input->hasActiveFocus());
2160 QCOMPARE(ic.openInputPanelReceived, true);
2161 ic.openInputPanelReceived = false;
2163 // no events on release
2164 QTest::mouseRelease(&view, Qt::LeftButton, 0, input->pos().toPoint());
2165 QCOMPARE(ic.openInputPanelReceived, false);
2166 ic.openInputPanelReceived = false;
2168 // if already focused, input panel can be opened on press
2169 QVERIFY(input->hasActiveFocus());
2170 QTest::mousePress(&view, Qt::LeftButton, 0, input->pos().toPoint());
2171 QApplication::processEvents();
2172 QCOMPARE(ic.openInputPanelReceived, true);
2173 ic.openInputPanelReceived = false;
2175 // input method should stay enabled if focus
2176 // is lost to an item that also accepts inputs
2177 QSGTextInput anotherInput;
2178 anotherInput.setParentItem(view.rootItem());
2179 anotherInput.setFocus(true);
2180 QApplication::processEvents();
2181 QCOMPARE(ic.openInputPanelReceived, true);
2182 ic.openInputPanelReceived = false;
2183 QCOMPARE(view.inputContext(), (QInputContext*)&ic);
2184 QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled));
2186 // input method should be disabled if focus
2187 // is lost to an item that doesn't accept inputs
2189 item.setParentItem(view.rootItem());
2190 item.setFocus(true);
2191 QApplication::processEvents();
2192 QCOMPARE(ic.openInputPanelReceived, false);
2193 QVERIFY(view.inputContext() == 0);
2194 QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
2196 // no automatic input panel events should
2197 // be sent if activeFocusOnPress is false
2198 input->setFocusOnPress(false);
2199 QCOMPARE(focusOnPressSpy.count(),1);
2200 input->setFocusOnPress(false);
2201 QCOMPARE(focusOnPressSpy.count(),1);
2202 input->setFocus(false);
2203 input->setFocus(true);
2204 QTest::mousePress(&view, Qt::LeftButton, 0, input->pos().toPoint());
2205 QTest::mouseRelease(&view, Qt::LeftButton, 0, input->pos().toPoint());
2206 QApplication::processEvents();
2207 QCOMPARE(ic.openInputPanelReceived, false);
2208 QCOMPARE(ic.closeInputPanelReceived, false);
2210 // one show input panel event should
2211 // be set when openSoftwareInputPanel is called
2212 input->openSoftwareInputPanel();
2213 QCOMPARE(ic.openInputPanelReceived, true);
2214 QCOMPARE(ic.closeInputPanelReceived, false);
2215 ic.openInputPanelReceived = false;
2217 // one close input panel event should
2218 // be sent when closeSoftwareInputPanel is called
2219 input->closeSoftwareInputPanel();
2220 QCOMPARE(ic.openInputPanelReceived, false);
2221 QCOMPARE(ic.closeInputPanelReceived, true);
2222 ic.closeInputPanelReceived = false;
2224 // set activeFocusOnPress back to true
2225 input->setFocusOnPress(true);
2226 QCOMPARE(focusOnPressSpy.count(),2);
2227 input->setFocusOnPress(true);
2228 QCOMPARE(focusOnPressSpy.count(),2);
2229 input->setFocus(false);
2230 QApplication::processEvents();
2231 QCOMPARE(ic.openInputPanelReceived, false);
2232 QCOMPARE(ic.closeInputPanelReceived, false);
2233 ic.closeInputPanelReceived = false;
2235 // input panel should not re-open
2236 // if focus has already been set
2237 input->setFocus(true);
2238 QCOMPARE(ic.openInputPanelReceived, true);
2239 ic.openInputPanelReceived = false;
2240 input->setFocus(true);
2241 QCOMPARE(ic.openInputPanelReceived, false);
2243 // input method should be disabled
2244 // if TextInput loses focus
2245 input->setFocus(false);
2246 QApplication::processEvents();
2247 QVERIFY(view.inputContext() == 0);
2248 QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
2250 // input method should not be enabled
2251 // if TextEdit is read only.
2252 input->setReadOnly(true);
2253 ic.openInputPanelReceived = false;
2254 input->setFocus(true);
2255 QApplication::processEvents();
2256 QCOMPARE(ic.openInputPanelReceived, false);
2257 QVERIFY(view.inputContext() == 0);
2258 QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
2261 class MyTextInput : public QSGTextInput
2264 MyTextInput(QSGItem *parent = 0) : QSGTextInput(parent)
2268 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2271 return QSGTextInput::updatePaintNode(node, data);
2276 void tst_qsgtextinput::setHAlignClearCache()
2280 input.setText("Hello world");
2281 input.setParentItem(view.rootItem());
2283 QApplication::setActiveWindow(&view);
2284 QTest::qWaitForWindowShown(&view);
2285 QTRY_COMPARE(input.nbPaint, 1);
2286 input.setHAlign(QSGTextInput::AlignRight);
2287 //Changing the alignment should trigger a repaint
2288 QTRY_COMPARE(input.nbPaint, 2);
2291 void tst_qsgtextinput::focusOutClearSelection()
2295 QSGTextInput input2;
2296 input.setText(QLatin1String("Hello world"));
2297 input.setFocus(true);
2298 input2.setParentItem(view.rootItem());
2299 input.setParentItem(view.rootItem());
2301 QApplication::setActiveWindow(&view);
2302 QTest::qWaitForWindowShown(&view);
2304 //The selection should work
2305 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2306 input2.setFocus(true);
2307 QApplication::processEvents();
2308 //The input lost the focus selection should be cleared
2309 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2312 void tst_qsgtextinput::geometrySignals()
2314 QDeclarativeComponent component(&engine, SRCDIR "/data/geometrySignals.qml");
2315 QObject *o = component.create();
2317 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2318 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2322 void tst_qsgtextinput::testQtQuick11Attributes()
2324 QFETCH(QString, code);
2325 QFETCH(QString, warning);
2326 QFETCH(QString, error);
2328 QDeclarativeEngine engine;
2331 QDeclarativeComponent valid(&engine);
2332 valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2333 obj = valid.create();
2335 QVERIFY(valid.errorString().isEmpty());
2338 QDeclarativeComponent invalid(&engine);
2339 invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2340 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2341 obj = invalid.create();
2342 QCOMPARE(invalid.errorString(), error);
2346 void tst_qsgtextinput::testQtQuick11Attributes_data()
2348 QTest::addColumn<QString>("code");
2349 QTest::addColumn<QString>("warning");
2350 QTest::addColumn<QString>("error");
2352 QTest::newRow("canPaste") << "property bool foo: canPaste"
2353 << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2356 QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2357 << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2360 QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2361 << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2365 void tst_qsgtextinput::preeditAutoScroll()
2367 QString preeditText = "califragisiticexpialidocious!";
2369 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/preeditAutoScroll.qml"));
2371 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2372 // and QWidget won't allow an input context to be set when the flag is not set.
2373 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2374 view.setInputContext(&ic);
2375 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2377 QApplication::setActiveWindow(&view);
2378 QTest::qWaitForWindowShown(&view);
2379 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2380 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2383 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2384 int cursorRectangleChanges = 0;
2386 QFontMetricsF fm(input->font());
2387 input->setWidth(fm.width(input->text()));
2389 // test the text is scrolled so the preedit is visible.
2390 ic.sendPreeditText(preeditText.mid(0, 3), 1);
2392 QEXPECT_FAIL("", "QTBUG-21011 fails", Abort);
2394 QVERIFY(input->positionAt(0) != 0);
2395 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2396 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2398 // test the text is scrolled back when the preedit is removed.
2399 ic.sendEvent(QInputMethodEvent());
2400 QCOMPARE(input->positionAt(0), 0);
2401 QCOMPARE(input->positionAt(input->width()), 5);
2402 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2404 // some tolerance for different fonts.
2406 const int error = 2;
2408 const int error = 5;
2411 // test if the preedit is larger than the text input that the
2412 // character preceding the cursor is still visible.
2413 qreal x = input->positionToRectangle(0).x();
2414 for (int i = 0; i < 3; ++i) {
2415 ic.sendPreeditText(preeditText, i + 1);
2416 QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2417 QVERIFY(input->positionToRectangle(0).x() < x);
2418 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2419 x = input->positionToRectangle(0).x();
2421 for (int i = 1; i >= 0; --i) {
2422 ic.sendPreeditText(preeditText, i + 1);
2423 QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2424 QVERIFY(input->positionToRectangle(0).x() > x);
2425 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2426 x = input->positionToRectangle(0).x();
2429 // Test incrementing the preedit cursor doesn't cause further
2430 // scrolling when right most text is visible.
2431 ic.sendPreeditText(preeditText, preeditText.length() - 3);
2432 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2433 x = input->positionToRectangle(0).x();
2434 for (int i = 2; i >= 0; --i) {
2435 ic.sendPreeditText(preeditText, preeditText.length() - i);
2436 QCOMPARE(input->positionToRectangle(0).x(), x);
2437 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2439 for (int i = 1; i < 3; ++i) {
2440 ic.sendPreeditText(preeditText, preeditText.length() - i);
2441 QCOMPARE(input->positionToRectangle(0).x(), x);
2442 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2445 // Test disabling auto scroll.
2446 ic.sendEvent(QInputMethodEvent());
2448 input->setAutoScroll(false);
2449 ic.sendPreeditText(preeditText.mid(0, 3), 1);
2450 QCOMPARE(input->positionAt(0), 0);
2451 QCOMPARE(input->positionAt(input->width()), 5);
2454 void tst_qsgtextinput::preeditMicroFocus()
2456 QString preeditText = "super";
2458 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputMethodEvent.qml"));
2460 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2461 // and QWidget won't allow an input context to be set when the flag is not set.
2462 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2463 view.setInputContext(&ic);
2464 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2466 QApplication::setActiveWindow(&view);
2467 QTest::qWaitForWindowShown(&view);
2468 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2469 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2473 QRect previousRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
2475 // Verify that the micro focus rect is positioned the same for position 0 as
2476 // it would be if there was no preedit text.
2477 ic.updateReceived = false;
2478 ic.sendPreeditText(preeditText, 0);
2479 currentRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
2480 QCOMPARE(currentRect, previousRect);
2481 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2482 QCOMPARE(ic.updateReceived, true);
2485 // Verify that the micro focus rect moves to the left as the cursor position
2487 for (int i = 1; i <= 5; ++i) {
2488 ic.updateReceived = false;
2489 ic.sendPreeditText(preeditText, i);
2490 currentRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
2491 QVERIFY(previousRect.left() < currentRect.left());
2492 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2493 QCOMPARE(ic.updateReceived, true);
2495 previousRect = currentRect;
2498 // Verify that if there is no preedit cursor then the micro focus rect is the
2499 // same as it would be if it were positioned at the end of the preedit text.
2500 ic.sendPreeditText(preeditText, 0);
2501 ic.updateReceived = false;
2502 ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2503 currentRect = input->inputMethodQuery(Qt::ImMicroFocus).toRect();
2504 QCOMPARE(currentRect, previousRect);
2505 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2506 QCOMPARE(ic.updateReceived, true);
2510 void tst_qsgtextinput::inputContextMouseHandler()
2512 QString text = "supercalifragisiticexpialidocious!";
2514 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
2516 // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2517 // and QWidget won't allow an input context to be set when the flag is not set.
2518 view.setAttribute(Qt::WA_InputMethodEnabled, true);
2519 view.setInputContext(&ic);
2520 view.setAttribute(Qt::WA_InputMethodEnabled, false);
2522 QApplication::setActiveWindow(&view);
2523 QTest::qWaitForWindowShown(&view);
2524 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2525 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2528 QFontMetricsF fm(input->font());
2529 const qreal y = fm.height() / 2;
2531 QPoint position2 = input->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
2532 QPoint position8 = input->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
2533 QPoint position20 = input->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
2534 QPoint position27 = input->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
2535 QPoint globalPosition2 = view.mapToGlobal(position2);
2536 QPoint globalposition8 = view.mapToGlobal(position8);
2537 QPoint globalposition20 = view.mapToGlobal(position20);
2538 QPoint globalposition27 = view.mapToGlobal(position27);
2540 ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2542 QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
2543 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2544 QCOMPARE(ic.eventPosition, position2);
2545 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2546 QCOMPARE(ic.eventButton, Qt::LeftButton);
2547 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2548 QVERIFY(ic.cursor < 0);
2549 ic.eventType = QEvent::None;
2551 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
2552 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2553 QCOMPARE(ic.eventPosition, position2);
2554 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2555 QCOMPARE(ic.eventButton, Qt::LeftButton);
2556 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2557 QVERIFY(ic.cursor < 0);
2558 ic.eventType = QEvent::None;
2560 { QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2561 QApplication::sendEvent(&view, &mv); }
2562 QCOMPARE(ic.eventType, QEvent::None);
2564 { QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2565 QApplication::sendEvent(&view, &mv); }
2566 QCOMPARE(ic.eventType, QEvent::MouseMove);
2567 QCOMPARE(ic.eventPosition, position27);
2568 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2569 QCOMPARE(ic.eventButton, Qt::LeftButton);
2570 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2571 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); // 15 is expected but some platforms may be off by one.
2572 ic.eventType = QEvent::None;
2574 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
2575 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2576 QCOMPARE(ic.eventPosition, position27);
2577 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2578 QCOMPARE(ic.eventButton, Qt::LeftButton);
2579 QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2580 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2581 ic.eventType = QEvent::None;
2583 // And in the other direction.
2584 QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
2585 QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2586 QCOMPARE(ic.eventPosition, position27);
2587 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2588 QCOMPARE(ic.eventButton, Qt::LeftButton);
2589 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2590 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2591 ic.eventType = QEvent::None;
2593 QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
2594 QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2595 QCOMPARE(ic.eventPosition, position27);
2596 QCOMPARE(ic.eventGlobalPosition, globalposition27);
2597 QCOMPARE(ic.eventButton, Qt::RightButton);
2598 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2599 QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2600 ic.eventType = QEvent::None;
2602 { QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2603 QApplication::sendEvent(&view, &mv); }
2604 QCOMPARE(ic.eventType, QEvent::MouseMove);
2605 QCOMPARE(ic.eventPosition, position20);
2606 QCOMPARE(ic.eventGlobalPosition, globalposition20);
2607 QCOMPARE(ic.eventButton, Qt::RightButton);
2608 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2609 QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2610 ic.eventType = QEvent::None;
2612 { QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2613 QApplication::sendEvent(&view, &mv); }
2614 QCOMPARE(ic.eventType, QEvent::None);
2616 QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
2617 QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2618 QCOMPARE(ic.eventPosition, position2);
2619 QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2620 QCOMPARE(ic.eventButton, Qt::RightButton);
2621 QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2622 QVERIFY(ic.cursor < 0);
2623 ic.eventType = QEvent::None;
2626 void tst_qsgtextinput::inputMethodComposing()
2628 QString text = "supercalifragisiticexpialidocious!";
2630 QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
2632 QApplication::setActiveWindow(&view);
2633 QTest::qWaitForWindowShown(&view);
2634 QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2635 QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2637 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2639 QCOMPARE(input->isInputMethodComposing(), false);
2641 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2642 QApplication::sendEvent(&view, &event);
2644 QCOMPARE(input->isInputMethodComposing(), true);
2645 QCOMPARE(spy.count(), 1);
2648 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2649 QApplication::sendEvent(&view, &event);
2651 QCOMPARE(spy.count(), 1);
2654 QInputMethodEvent event;
2655 QApplication::sendEvent(&view, &event);
2657 QCOMPARE(input->isInputMethodComposing(), false);
2658 QCOMPARE(spy.count(), 2);
2661 void tst_qsgtextinput::cursorRectangleSize()
2663 QSGView *canvas = new QSGView(QUrl::fromLocalFile(SRCDIR "/data/positionAt.qml"));
2664 QVERIFY(canvas->rootObject() != 0);
2667 QApplication::setActiveWindow(canvas);
2668 QTest::qWaitForWindowShown(canvas);
2670 QSGTextInput *textInput = qobject_cast<QSGTextInput *>(canvas->rootObject());
2671 QVERIFY(textInput != 0);
2672 textInput->setFocus(Qt::OtherFocusReason);
2673 QRectF cursorRect = textInput->positionToRectangle(textInput->cursorPosition());
2674 QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImMicroFocus).toRectF();
2675 QRectF microFocusFromApp= QApplication::focusWidget()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
2677 QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2678 QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2683 QTEST_MAIN(tst_qsgtextinput)
2685 #include "tst_qsgtextinput.moc"