tst_qquicktextinput.cpp 209 KB
Newer Older
1 2
/****************************************************************************
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/
5 6 7 8 9
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
10 11 12 13 14 15
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 17
**
** In addition, as a special exception, Nokia gives you certain additional
18
** rights. These rights are described in the Nokia Qt LGPL Exception
19 20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
21 22 23 24 25 26 27 28 29 30 31
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
32 33 34 35 36
**
**
**
**
**
37
**
38 39 40 41 42
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include <QtTest/QSignalSpy>
43
#include "../../shared/util.h"
44
#include <private/qinputmethod_p.h>
45 46
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlexpression.h>
47
#include <QFile>
48
#include <QtQuick/qquickview.h>
49
#include <QtGui/qguiapplication.h>
50
#include <QtGui/qstylehints.h>
51
#include <QInputMethod>
52 53
#include <private/qquicktextinput_p.h>
#include <private/qquicktextinput_p_p.h>
54 55
#include <QDebug>
#include <QDir>
56
#include <math.h>
57

58 59 60 61
#ifdef Q_OS_MAC
#include <Carbon/Carbon.h>
#endif

62
#include "qplatformdefs.h"
63
#include "../../shared/platforminputcontext.h"
64

65
Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
66 67 68
Q_DECLARE_METATYPE(QQuickTextInput::EchoMode)
Q_DECLARE_METATYPE(Qt::Key)

69
DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
70 71 72 73

QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
{
    // XXX This will be replaced by some clever persistent platform image store.
74
    QString persistent_dir = QQmlDataTest::instance()->dataDirectory();
75 76 77 78 79 80 81 82 83 84 85 86
    QString arch = "unknown-architecture"; // QTest needs to help with this.

    QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";

    if (!QFile::exists(expectfile)) {
        actual.save(expectfile);
        qWarning() << "created" << expectfile;
    }

    return expectfile;
}

87 88
template <typename T> static T evaluate(QObject *scope, const QString &expression)
{
89
    QQmlExpression expr(qmlContext(scope), scope, expression);
90 91 92 93 94 95
    T result = expr.evaluate().value<T>();
    if (expr.hasError())
        qWarning() << expr.error().toString();
    return result;
}

96 97
template<typename T, int N> int lengthOf(const T (&)[N]) { return N; }

98 99
typedef QPair<int, QChar> Key;

100
class tst_qquicktextinput : public QQmlDataTest
101 102 103 104

{
    Q_OBJECT
public:
105
    tst_qquicktextinput();
106 107

private slots:
108
    void cleanup();
109 110 111 112
    void text();
    void width();
    void font();
    void color();
113
    void wrap();
114
    void selection();
115
    void persistentSelection();
116 117 118 119 120 121 122 123 124
    void isRightToLeft_data();
    void isRightToLeft();
    void moveCursorSelection_data();
    void moveCursorSelection();
    void moveCursorSelectionSequence_data();
    void moveCursorSelectionSequence();
    void dragMouseSelection();
    void mouseSelectionMode_data();
    void mouseSelectionMode();
125
    void tripleClickSelectsAll();
126 127 128 129

    void horizontalAlignment_data();
    void horizontalAlignment();
    void horizontalAlignment_RightToLeft();
130
    void verticalAlignment();
131

132 133
    void boundingRect();

134 135 136 137 138 139 140 141
    void positionAt();

    void maxLength();
    void masks();
    void validators();
    void inputMethods();

    void passwordCharacter();
142
    void cursorDelegate_data();
143 144
    void cursorDelegate();
    void cursorVisible();
145
    void cursorRectangle_data();
146 147 148 149
    void cursorRectangle();
    void navigation();
    void navigation_RTL();
    void copyAndPaste();
150
    void copyAndPasteKeySequence();
151 152 153
    void canPasteEmpty();
    void canPaste();
    void readOnly();
154
    void focusOnPress();
155

156
    void openInputPanel();
157 158 159 160
    void setHAlignClearCache();
    void focusOutClearSelection();

    void echoMode();
161 162 163
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
    void passwordEchoDelay();
#endif
164
    void geometrySignals();
165
    void contentSize();
166 167

    void preeditAutoScroll();
168
    void preeditCursorRectangle();
169
    void inputContextMouseHandler();
170
    void inputMethodComposing();
171
    void inputMethodUpdate();
172 173
    void cursorRectangleSize();

174 175 176 177 178 179 180
    void getText_data();
    void getText();
    void insert_data();
    void insert();
    void remove_data();
    void remove();

181 182 183 184 185 186 187 188 189 190
    void keySequence_data();
    void keySequence();

    void undo_data();
    void undo();
    void redo_data();
    void redo();
    void undo_keypressevents_data();
    void undo_keypressevents();

191 192 193 194
    void QTBUG_19956();
    void QTBUG_19956_data();
    void QTBUG_19956_regexp();

195 196 197 198 199
    void implicitSize_data();
    void implicitSize();
    void implicitSizeBinding_data();
    void implicitSizeBinding();

200 201
    void negativeDimensions();

202 203 204 205 206 207 208 209 210 211 212 213 214

    void setInputMask_data();
    void setInputMask();
    void inputMask_data();
    void inputMask();
    void clearInputMask();
    void keypress_inputMask_data();
    void keypress_inputMask();
    void hasAcceptableInputMask_data();
    void hasAcceptableInputMask();
    void maskCharacter_data();
    void maskCharacter();

215
private:
216
    void simulateKey(QWindow *, int key);
217

218 219 220
    void simulateKeys(QWindow *window, const QList<Key> &keys);
    void simulateKeys(QWindow *window, const QKeySequence &sequence);

221
    QQmlEngine engine;
222 223 224
    QStringList standard;
    QStringList colorStrings;
};
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

typedef QList<int> IntList;
Q_DECLARE_METATYPE(IntList)

typedef QList<Key> KeyList;
Q_DECLARE_METATYPE(KeyList)

void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
{
    for (int i = 0; i < keys.count(); ++i) {
        const int key = keys.at(i).first;
        const int modifiers = key & Qt::KeyboardModifierMask;
        const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();

        QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
        QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);

        QGuiApplication::sendEvent(window, &press);
        QGuiApplication::sendEvent(window, &release);
    }
}

void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
{
249
    for (int i = 0; i < sequence.count(); ++i) {
250 251 252 253 254 255 256 257 258
        const int key = sequence[i];
        const int modifiers = key & Qt::KeyboardModifierMask;

        QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
    }
}

QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
{
259
    for (int i = 0; i < sequence.count(); ++i)
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
        keys << Key(sequence[i], QChar());
    return keys;
}

template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
{
    for (int i = 0; i < N - 1; ++i) {
        int key = QTest::asciiToKey(characters[i]);
        QChar character = QLatin1Char(characters[i]);
        keys << Key(key, character);
    }
    return keys;
}

QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
{
    keys << Key(key, QChar());
    return keys;
}

280 281 282
void tst_qquicktextinput::cleanup()
{
    // ensure not even skipped tests with custom input context leave it dangling
283 284
    QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
    inputMethodPrivate->testContext = 0;
285
}
286

287
tst_qquicktextinput::tst_qquicktextinput()
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
{
    standard << "the quick brown fox jumped over the lazy dog"
        << "It's supercalifragisiticexpialidocious!"
        << "Hello, world!"
        << "!dlrow ,olleH"
        << " spacey   text ";

    colorStrings << "aliceblue"
                 << "antiquewhite"
                 << "aqua"
                 << "darkkhaki"
                 << "darkolivegreen"
                 << "dimgray"
                 << "palevioletred"
                 << "lightsteelblue"
                 << "#000000"
                 << "#AAAAAA"
                 << "#FFFFFF"
                 << "#2AC05F";
}

309
void tst_qquicktextinput::text()
310 311
{
    {
312
        QQmlComponent textinputComponent(&engine);
313
        textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\"  }", QUrl());
314
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
315 316 317

        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->text(), QString(""));
318
        QCOMPARE(textinputObject->length(), 0);
319 320 321 322 323 324 325

        delete textinputObject;
    }

    for (int i = 0; i < standard.size(); i++)
    {
        QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
326
        QQmlComponent textinputComponent(&engine);
327
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
328
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
329 330 331

        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->text(), standard.at(i));
332
        QCOMPARE(textinputObject->length(), standard.at(i).length());
333 334 335 336 337 338

        delete textinputObject;
    }

}

339
void tst_qquicktextinput::width()
340 341 342
{
    // uses Font metrics to find the width for standard
    {
343
        QQmlComponent textinputComponent(&engine);
344
        textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\" }", QUrl());
345
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
346 347 348 349 350 351 352

        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->width(), 0.0);

        delete textinputObject;
    }

353 354
    bool requiresUnhintedMetrics = !qmlDisableDistanceField();

355 356 357
    for (int i = 0; i < standard.size(); i++)
    {
        QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
358
        QQmlComponent textinputComponent(&engine);
359
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
360
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
361

362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
        QString s = standard.at(i);
        s.replace(QLatin1Char('\n'), QChar::LineSeparator);

        QTextLayout layout(s);
        layout.setFont(textinputObject->font());
        layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
        if (requiresUnhintedMetrics) {
            QTextOption option;
            option.setUseDesignMetrics(true);
            layout.setTextOption(option);
        }

        layout.beginLayout();
        forever {
            QTextLine line = layout.createLine();
            if (!line.isValid())
                break;
        }

        layout.endLayout();

        qreal metricWidth = ceil(layout.boundingRect().width());

385 386 387 388 389 390 391 392
        QVERIFY(textinputObject != 0);
        int delta = abs(int(int(textinputObject->width()) - metricWidth));
        QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.

        delete textinputObject;
    }
}

393
void tst_qquicktextinput::font()
394 395
{
    //test size, then bold, then italic, then family
Kent Hansen's avatar
Kent Hansen committed
396
    {
397
        QString componentStr = "import QtQuick 2.0\nTextInput {  font.pointSize: 40; text: \"Hello World\" }";
398
        QQmlComponent textinputComponent(&engine);
399
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
400
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
401 402 403 404 405 406 407 408 409

        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->font().pointSize(), 40);
        QCOMPARE(textinputObject->font().bold(), false);
        QCOMPARE(textinputObject->font().italic(), false);

        delete textinputObject;
    }

Kent Hansen's avatar
Kent Hansen committed
410
    {
411
        QString componentStr = "import QtQuick 2.0\nTextInput {  font.bold: true; text: \"Hello World\" }";
412
        QQmlComponent textinputComponent(&engine);
413
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
414
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
415 416 417 418 419 420 421 422

        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->font().bold(), true);
        QCOMPARE(textinputObject->font().italic(), false);

        delete textinputObject;
    }

Kent Hansen's avatar
Kent Hansen committed
423
    {
424
        QString componentStr = "import QtQuick 2.0\nTextInput {  font.italic: true; text: \"Hello World\" }";
425
        QQmlComponent textinputComponent(&engine);
426
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
427
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
428 429 430 431 432 433 434

        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->font().italic(), true);
        QCOMPARE(textinputObject->font().bold(), false);

        delete textinputObject;
    }
Kent Hansen's avatar
Kent Hansen committed
435 436

    {
437
        QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"Helvetica\"; text: \"Hello World\" }";
438
        QQmlComponent textinputComponent(&engine);
439
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
440
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
441 442 443 444 445 446 447 448 449

        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
        QCOMPARE(textinputObject->font().bold(), false);
        QCOMPARE(textinputObject->font().italic(), false);

        delete textinputObject;
    }

Kent Hansen's avatar
Kent Hansen committed
450
    {
451
        QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"\"; text: \"Hello World\" }";
452
        QQmlComponent textinputComponent(&engine);
453
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
454
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
455 456 457 458 459 460 461 462

        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->font().family(), QString(""));

        delete textinputObject;
    }
}

463
void tst_qquicktextinput::color()
464 465 466
{
    //test color
    for (int i = 0; i < colorStrings.size(); i++)
Kent Hansen's avatar
Kent Hansen committed
467
    {
468
        QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
469
        QQmlComponent textinputComponent(&engine);
470
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
471
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
472 473 474 475 476 477 478 479 480 481
        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));

        delete textinputObject;
    }

    //test selection color
    for (int i = 0; i < colorStrings.size(); i++)
    {
        QString componentStr = "import QtQuick 2.0\nTextInput {  selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
482
        QQmlComponent textinputComponent(&engine);
483
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
484
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
485 486 487 488 489 490 491 492
        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));

        delete textinputObject;
    }

    //test selected text color
    for (int i = 0; i < colorStrings.size(); i++)
Kent Hansen's avatar
Kent Hansen committed
493
    {
494
        QString componentStr = "import QtQuick 2.0\nTextInput {  selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
495
        QQmlComponent textinputComponent(&engine);
496
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
497
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
498 499 500 501 502 503 504 505 506 507 508 509
        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));

        delete textinputObject;
    }

    {
        QString colorStr = "#AA001234";
        QColor testColor("#001234");
        testColor.setAlpha(170);

        QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStr + "\"; text: \"Hello World\" }";
510
        QQmlComponent textinputComponent(&engine);
511
        textinputComponent.setData(componentStr.toLatin1(), QUrl());
512
        QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
513 514 515 516 517 518 519 520

        QVERIFY(textinputObject != 0);
        QCOMPARE(textinputObject->color(), testColor);

        delete textinputObject;
    }
}

521 522 523 524 525
void tst_qquicktextinput::wrap()
{
    int textHeight = 0;
    // for specified width and wrap set true
    {
526
        QQmlComponent textComponent(&engine);
527 528 529 530 531 532 533 534 535 536 537 538 539
        textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
        QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
        textHeight = textObject->height();

        QVERIFY(textObject != 0);
        QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
        QCOMPARE(textObject->width(), 300.);

        delete textObject;
    }

    for (int i = 0; i < standard.count(); i++) {
        QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
540
        QQmlComponent textComponent(&engine);
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
        textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
        QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());

        QVERIFY(textObject != 0);
        QCOMPARE(textObject->width(), 30.);
        QVERIFY(textObject->height() > textHeight);

        int oldHeight = textObject->height();
        textObject->setWidth(100);
        QVERIFY(textObject->height() < oldHeight);

        delete textObject;
    }
}

556
void tst_qquicktextinput::selection()
557 558 559
{
    QString testStr = standard[0];
    QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
560
    QQmlComponent textinputComponent(&engine);
561
    textinputComponent.setData(componentStr.toLatin1(), QUrl());
562
    QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
563 564 565 566
    QVERIFY(textinputObject != 0);


    //Test selection follows cursor
Kent Hansen's avatar
Kent Hansen committed
567
    for (int i=0; i<= testStr.size(); i++) {
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
        textinputObject->setCursorPosition(i);
        QCOMPARE(textinputObject->cursorPosition(), i);
        QCOMPARE(textinputObject->selectionStart(), i);
        QCOMPARE(textinputObject->selectionEnd(), i);
        QVERIFY(textinputObject->selectedText().isNull());
    }

    textinputObject->setCursorPosition(0);
    QVERIFY(textinputObject->cursorPosition() == 0);
    QVERIFY(textinputObject->selectionStart() == 0);
    QVERIFY(textinputObject->selectionEnd() == 0);
    QVERIFY(textinputObject->selectedText().isNull());

    // Verify invalid positions are ignored.
    textinputObject->setCursorPosition(-1);
    QVERIFY(textinputObject->cursorPosition() == 0);
    QVERIFY(textinputObject->selectionStart() == 0);
    QVERIFY(textinputObject->selectionEnd() == 0);
    QVERIFY(textinputObject->selectedText().isNull());

    textinputObject->setCursorPosition(textinputObject->text().count()+1);
    QVERIFY(textinputObject->cursorPosition() == 0);
    QVERIFY(textinputObject->selectionStart() == 0);
    QVERIFY(textinputObject->selectionEnd() == 0);
    QVERIFY(textinputObject->selectedText().isNull());

    //Test selection
Kent Hansen's avatar
Kent Hansen committed
595
    for (int i=0; i<= testStr.size(); i++) {
596 597 598
        textinputObject->select(0,i);
        QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
    }
Kent Hansen's avatar
Kent Hansen committed
599
    for (int i=0; i<= testStr.size(); i++) {
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
        textinputObject->select(i,testStr.size());
        QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
    }

    textinputObject->setCursorPosition(0);
    QVERIFY(textinputObject->cursorPosition() == 0);
    QVERIFY(textinputObject->selectionStart() == 0);
    QVERIFY(textinputObject->selectionEnd() == 0);
    QVERIFY(textinputObject->selectedText().isNull());

    //Test Error Ignoring behaviour
    textinputObject->setCursorPosition(0);
    QVERIFY(textinputObject->selectedText().isNull());
    textinputObject->select(-10,0);
    QVERIFY(textinputObject->selectedText().isNull());
    textinputObject->select(100,110);
    QVERIFY(textinputObject->selectedText().isNull());
    textinputObject->select(0,-10);
    QVERIFY(textinputObject->selectedText().isNull());
    textinputObject->select(0,100);
    QVERIFY(textinputObject->selectedText().isNull());
    textinputObject->select(0,10);
    QVERIFY(textinputObject->selectedText().size() == 10);
    textinputObject->select(-10,10);
    QVERIFY(textinputObject->selectedText().size() == 10);
    textinputObject->select(100,101);
    QVERIFY(textinputObject->selectedText().size() == 10);
    textinputObject->select(0,-10);
    QVERIFY(textinputObject->selectedText().size() == 10);
    textinputObject->select(0,100);
    QVERIFY(textinputObject->selectedText().size() == 10);

    textinputObject->deselect();
    QVERIFY(textinputObject->selectedText().isNull());
    textinputObject->select(0,10);
    QVERIFY(textinputObject->selectedText().size() == 10);
    textinputObject->deselect();
    QVERIFY(textinputObject->selectedText().isNull());

639 640 641 642 643 644 645
    // test input method selection
    QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
    textinputObject->setFocus(true);
    {
        QList<QInputMethodEvent::Attribute> attributes;
        attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
        QInputMethodEvent event("", attributes);
646
        QGuiApplication::sendEvent(textinputObject, &event);
647 648 649 650 651
    }
    QCOMPARE(selectionSpy.count(), 1);
    QCOMPARE(textinputObject->selectionStart(), 12);
    QCOMPARE(textinputObject->selectionEnd(), 17);

652 653 654
    delete textinputObject;
}

655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
void tst_qquicktextinput::persistentSelection()
{
    QQuickView canvas(testFileUrl("persistentSelection.qml"));
    canvas.show();
    canvas.requestActivateWindow();
    QTest::qWaitForWindowShown(&canvas);
    QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
    canvas.requestActivateWindow();

    QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
    QVERIFY(input);
    QVERIFY(input->hasActiveFocus());

    QSignalSpy spy(input, SIGNAL(persistentSelectionChanged()));

    QCOMPARE(input->persistentSelection(), false);

    input->setPersistentSelection(false);
    QCOMPARE(input->persistentSelection(), false);
    QCOMPARE(spy.count(), 0);

    input->select(1, 4);
    QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));

    input->setFocus(false);
    QCOMPARE(input->property("selected").toString(), QString());

    input->setFocus(true);
    QCOMPARE(input->property("selected").toString(), QString());

    input->setPersistentSelection(true);
    QCOMPARE(input->persistentSelection(), true);
    QCOMPARE(spy.count(), 1);

    input->select(1, 4);
    QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));

    input->setFocus(false);
    QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));

    input->setFocus(true);
    QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
}

699
void tst_qquicktextinput::isRightToLeft_data()
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
{
    QTest::addColumn<QString>("text");
    QTest::addColumn<bool>("emptyString");
    QTest::addColumn<bool>("firstCharacter");
    QTest::addColumn<bool>("lastCharacter");
    QTest::addColumn<bool>("middleCharacter");
    QTest::addColumn<bool>("startString");
    QTest::addColumn<bool>("midString");
    QTest::addColumn<bool>("endString");

    const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
    QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
    QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
    QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
    QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
    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;
    QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
}

719
void tst_qquicktextinput::isRightToLeft()
720 721 722 723 724 725 726 727 728 729
{
    QFETCH(QString, text);
    QFETCH(bool, emptyString);
    QFETCH(bool, firstCharacter);
    QFETCH(bool, lastCharacter);
    QFETCH(bool, middleCharacter);
    QFETCH(bool, startString);
    QFETCH(bool, midString);
    QFETCH(bool, endString);

730
    QQuickTextInput textInput;
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
    textInput.setText(text);

    // first test that the right string is delivered to the QString::isRightToLeft()
    QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
    QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
    QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
    QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
    QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
    QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
    if (text.isEmpty())
        QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
    QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());

    // then test that the feature actually works
    QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
    QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
    QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
    QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
    QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
    QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
    if (text.isEmpty())
        QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
    QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
}

756
void tst_qquicktextinput::moveCursorSelection_data()
757 758 759 760
{
    QTest::addColumn<QString>("testStr");
    QTest::addColumn<int>("cursorPosition");
    QTest::addColumn<int>("movePosition");
761
    QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
762 763 764 765 766 767 768 769
    QTest::addColumn<int>("selectionStart");
    QTest::addColumn<int>("selectionEnd");
    QTest::addColumn<bool>("reversible");

    // () contains the text selected by the cursor.
    // <> contains the actual selection.

    QTest::newRow("(t)he|characters")
770
            << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
771
    QTest::newRow("do(g)|characters")
772
            << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
773
    QTest::newRow("jum(p)ed|characters")
774
            << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
775
    QTest::newRow("jumped( )over|characters")
776
            << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
777
    QTest::newRow("(the )|characters")
778
            << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
779
    QTest::newRow("( dog)|characters")
780
            << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
781
    QTest::newRow("( jumped )|characters")
782
            << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
783
    QTest::newRow("th(e qu)ick|characters")
784
            << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
785
    QTest::newRow("la(zy d)og|characters")
786
            << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
787
    QTest::newRow("jum(ped ov)er|characters")
788
            << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
789
    QTest::newRow("()the|characters")
790
            << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
791
    QTest::newRow("dog()|characters")
792
            << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
793
    QTest::newRow("jum()ped|characters")
794
            << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
795 796

    QTest::newRow("<(t)he>|words")
797
            << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
798
    QTest::newRow("<do(g)>|words")
799
            << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
800
    QTest::newRow("<jum(p)ed>|words")
801
            << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
802
    QTest::newRow("<jumped( )>over|words,ltr")
803
            << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
804
    QTest::newRow("jumped<( )over>|words,rtl")
805
            << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
806
    QTest::newRow("<(the )>quick|words,ltr")
807
            << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
808
    QTest::newRow("<(the )quick>|words,rtl")
809
            << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
810
    QTest::newRow("<lazy( dog)>|words,ltr")
811
            << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
812
    QTest::newRow("lazy<( dog)>|words,rtl")
813
            << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
814
    QTest::newRow("<fox( jumped )>over|words,ltr")
815
            << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
816
    QTest::newRow("fox<( jumped )over>|words,rtl")
817
            << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
818
    QTest::newRow("<th(e qu)ick>|words")
819
            << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
820
    QTest::newRow("<la(zy d)og|words>")
821
            << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
822
    QTest::newRow("<jum(ped ov)er>|words")
823
            << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
824
    QTest::newRow("<()>the|words")
825
            << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
826
    QTest::newRow("dog<()>|words")
827
            << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
828
    QTest::newRow("jum<()>ped|words")
829
            << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
830 831

    QTest::newRow("Hello<(,)> |words")
832
            << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
833
    QTest::newRow("Hello<(, )>world|words,ltr")
834
            << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
835
    QTest::newRow("Hello<(, )world>|words,rtl")
836
            << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
837
    QTest::newRow("<Hel(lo, )>world|words,ltr")
838
            << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
839
    QTest::newRow("<Hel(lo, )world>|words,rtl")
840
            << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
841
    QTest::newRow("<Hel(lo)>,|words")
842
            << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
843
    QTest::newRow("Hello<()>,|words")
844
            << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
845
    QTest::newRow("Hello,<()>|words")
846
            << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
847
    QTest::newRow("Hello<,( )>world|words,ltr")
848
            << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
849
    QTest::newRow("Hello,<( )world>|words,rtl")
850
            << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
851
    QTest::newRow("Hello<,( world)>|words,ltr")
852
            << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
853
    QTest::newRow("Hello,<( world)>|words,rtl")
854
            << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
855
    QTest::newRow("Hello<,( world!)>|words,ltr")
856
            << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
857
    QTest::newRow("Hello,<( world!)>|words,rtl")
858
            << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
859
    QTest::newRow("Hello<(, world!)>|words")
860
            << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
861 862 863
    // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
    // QTBUG-11365
    // QTest::newRow("world<(!)>|words")
864
    //         << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
865
    QTest::newRow("world!<()>)|words")
866
            << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
867
    QTest::newRow("world<()>!)|words")
868
            << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
869 870

    QTest::newRow("<(,)>olleH |words")
871
            << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
872
    QTest::newRow("<dlrow( ,)>olleH|words,ltr")
873
            << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
874
    QTest::newRow("dlrow<( ,)>olleH|words,rtl")
875
            << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
876
    QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
877
            << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
878
    QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
879
            << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
880
    QTest::newRow(",<(ol)leH>,|words")
881
            << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
882
    QTest::newRow(",<()>olleH|words")
883
            << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
884
    QTest::newRow("<()>,olleH|words")
885
            << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
886
    QTest::newRow("<dlrow( )>,olleH|words,ltr")
887
            << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
888
    QTest::newRow("dlrow<( ),>olleH|words,rtl")
889
            << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
890
    QTest::newRow("<(dlrow )>,olleH|words,ltr")
891
            << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
892
    QTest::newRow("<(dlrow ),>olleH|words,rtl")
893
            << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
894
    QTest::newRow("<(!dlrow )>,olleH|words,ltr")
895
            << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
896
    QTest::newRow("<(!dlrow ),>olleH|words,rtl")
897
            << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
898
    QTest::newRow("(!dlrow ,)olleH|words")
899
            << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
900
    QTest::newRow("<(!)>dlrow|words")
901
            << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
902
    QTest::newRow("<()>!dlrow|words")
903
            << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
904
    QTest::newRow("!<()>dlrow|words")
905
            << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
906 907

    QTest::newRow(" <s(pac)ey>   text |words")
908
            << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
909
    QTest::newRow(" spacey   <t(ex)t> |words")
910
            << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
911
    QTest::newRow("<( )>spacey   text |words|ltr")
912
            << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
913
    QTest::newRow("<( )spacey>   text |words|rtl")
914
            << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
915
    QTest::newRow("spacey   <text( )>|words|ltr")
916
            << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
917 918
//    QTBUG-11365
//    QTest::newRow("spacey   text<( )>|words|rtl")
919
//            << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
920
    QTest::newRow("<()> spacey   text |words")
921
            << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
922
    QTest::newRow(" spacey   text <()>|words")
923
            << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
924 925
}

926
void tst_qquicktextinput::moveCursorSelection()
927 928 929 930
{
    QFETCH(QString, testStr);
    QFETCH(int, cursorPosition);
    QFETCH(int, movePosition);
931
    QFETCH(QQuickTextInput::SelectionMode, mode);
932 933 934 935 936
    QFETCH(int, selectionStart);
    QFETCH(int, selectionEnd);
    QFETCH(bool, reversible);

    QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
937
    QQmlComponent textinputComponent(&engine);
938
    textinputComponent.setData(componentStr.toLatin1(), QUrl());
939
    QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
    QVERIFY(textinputObject != 0);

    textinputObject->setCursorPosition(cursorPosition);
    textinputObject->moveCursorSelection(movePosition, mode);

    QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
    QCOMPARE(textinputObject->selectionStart(), selectionStart);
    QCOMPARE(textinputObject->selectionEnd(), selectionEnd);

    if (reversible) {
        textinputObject->setCursorPosition(movePosition);
        textinputObject->moveCursorSelection(cursorPosition, mode);

        QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
        QCOMPARE(textinputObject->selectionStart(), selectionStart);
        QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
    }

    delete textinputObject;
}

961
void tst_qquicktextinput::moveCursorSelectionSequence_data()
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134
{
    QTest::addColumn<QString>("testStr");
    QTest::addColumn<int>("cursorPosition");
    QTest::addColumn<int>("movePosition1");
    QTest::addColumn<int>("movePosition2");
    QTest::addColumn<int>("selection1Start");
    QTest::addColumn<int>("selection1End");
    QTest::addColumn<int>("selection2Start");
    QTest::addColumn<int>("selection2End");

    // () contains the text selected by the cursor.
    // <> contains the actual selection.
    // ^ is the revised cursor position.
    // {} contains the revised selection.

    QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
            << standard[0]
            << 9 << 13 << 17
            << 4 << 15
            << 4 << 19;
    QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
            << standard[0]
            << 13 << 9 << 17
            << 9 << 15
            << 10 << 19;
    QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
            << standard[0]
            << 9 << 13 << 16
            << 4 << 15
            << 4 << 16;
    QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
            << standard[0]
            << 13 << 9 << 16
            << 9 << 15
            << 10 << 16;
    QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
            << standard[0]
            << 9 << 13 << 15
            << 4 << 15
            << 4 << 15;
    QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
            << standard[0]
            << 13 << 9 << 15
            << 9 << 15
            << 10 << 15;
    QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
            << standard[0]
            << 9 << 13 << 10
            << 4 << 15
            << 4 << 10;
    QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
            << standard[0]
            << 13 << 9 << 10
            << 9 << 15
            << 10 << 15;
    QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
            << standard[0]
            << 9 << 13 << 9
            << 4 << 15
            << 4 << 9;
    QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
            << standard[0]
            << 13 << 9 << 9
            << 9 << 15
            << 9 << 15;
    QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
            << standard[0]
            << 9 << 13 << 7
            << 4 << 15
            << 4 << 9;
    QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
            << standard[0]
            << 13 << 9 << 7
            << 9 << 15
            << 4 << 15;
    QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
            << standard[0]
            << 9 << 13 << 4
            << 4 << 15
            << 4 << 9;
    QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
            << standard[0]
            << 13 << 9 << 4
            << 9 << 15
            << 4 << 15;
    QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
            << standard[0]
            << 9 << 13 << 3
            << 4 << 15
            << 3 << 9;
    QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
            << standard[0]
            << 13 << 9 << 3
            << 9 << 15
            << 3 << 15;
    QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
            << standard[0]
            << 9 << 13 << 1
            << 4 << 15
            << 0 << 9;
    QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
            << standard[0]
            << 13 << 9 << 1
            << 9 << 15
            << 0 << 15;

    QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
            << standard[2]
            << 2 << 4 << 8
            << 0 << 5
            << 0 << 12;
    QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
            << standard[2]
            << 4 << 2 << 8
            << 0 << 5
            << 0 << 12;

    QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
            << standard[3]
            << 9 << 11 << 5
            << 8 << 13
            << 1 << 13;
    QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
            << standard[3]
            << 11 << 9 << 5
            << 8 << 13
            << 1 << 13;

    QTest::newRow("{<(^} sp)acey>   text |ltr")
            << standard[4]
            << 0 << 3 << 0
            << 0 << 7
            << 0 << 0;
    QTest::newRow("{<( ^}sp)acey>   text |ltr")
            << standard[4]
            << 0 << 3 << 1
            << 0 << 7
            << 0 << 1;
    QTest::newRow("<( {s^p)acey>}   text |rtl")
            << standard[4]
            << 3 << 0 << 2
            << 0 << 7
            << 1 << 7;
    QTest::newRow("<( {^sp)acey>}   text |rtl")
            << standard[4]
            << 3 << 0 << 1
            << 0 << 7
            << 1 << 7;

    QTest::newRow(" spacey   <te(xt {^)>}|rtl")
            << standard[4]
            << 15 << 12 << 15
            << 10 << 15
            << 15 << 15;
//    QTBUG-11365
//    QTest::newRow(" spacey   <te(xt{^ )>}|rtl")
//            << standard[4]
//            << 15 << 12 << 14
//            << 10 << 15
//            << 14 << 15;
    QTest::newRow(" spacey   {<te(x^t} )>|ltr")
            << standard[4]
            << 12 << 15 << 13
            << 10 << 15
            << 10 << 14;
//    QTBUG-11365
//    QTest::newRow(" spacey   {<te(xt^} )>|ltr")
//            << standard[4]
//            << 12 << 15 << 14
//            << 10 << 15
//            << 10 << 14;
}

1135
void tst_qquicktextinput::moveCursorSelectionSequence()
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
{
    QFETCH(QString, testStr);
    QFETCH(int, cursorPosition);
    QFETCH(int, movePosition1);
    QFETCH(int, movePosition2);
    QFETCH(int, selection1Start);
    QFETCH(int, selection1End);
    QFETCH(int, selection2Start);
    QFETCH(int, selection2End);

    QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
1147
    QQmlComponent textinputComponent(&engine);
1148
    textinputComponent.setData(componentStr.toLatin1(), QUrl());
1149
    QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1150 1151 1152 1153
    QVERIFY(textinputObject != 0);

    textinputObject->setCursorPosition(cursorPosition);

1154
    textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1155 1156 1157 1158
    QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
    QCOMPARE(textinputObject->selectionStart(), selection1Start);
    QCOMPARE(textinputObject->selectionEnd(), selection1End);

1159
    textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1160 1161 1162 1163 1164 1165 1166
    QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
    QCOMPARE(textinputObject->selectionStart(), selection2Start);
    QCOMPARE(textinputObject->selectionEnd(), selection2End);

    delete textinputObject;
}

1167
void tst_qquicktextinput::dragMouseSelection()
1168
{
1169
    QString qmlfile = testFile("mouseselection_true.qml");
1170

1171
    QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1172 1173

    canvas.show();
1174
    canvas.requestActivateWindow();
1175
    QTest::qWaitForWindowShown(&canvas);
1176

1177
    QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1178 1179

    QVERIFY(canvas.rootObject() != 0);
1180
    QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1181 1182 1183 1184 1185 1186 1187
    QVERIFY(textInputObject != 0);

    // press-and-drag-and-release from x1 to x2
    int x1 = 10;
    int x2 = 70;
    int y = textInputObject->height()/2;
    QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1188
    QTest::mouseMove(&canvas, QPoint(x2, y));
1189
    QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1190 1191 1192
    QTest::qWait(100);
    QString str1;
    QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1193 1194 1195 1196 1197 1198
    QVERIFY(str1.length() > 3);

    // press and drag the current selection.
    x1 = 40;
    x2 = 100;
    QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1199 1200 1201
    QTest::mouseMove(&canvas, QPoint(x2, y));
    QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
    QTest::qWait(300);
1202 1203 1204
    QString str2 = textInputObject->selectedText();
    QVERIFY(str2.length() > 3);

1205
    QVERIFY(str1 != str2);
1206 1207
}

1208
void tst_qquicktextinput::mouseSelectionMode_data()
1209 1210 1211
{
    QTest::addColumn<QString>("qmlfile");
    QTest::addColumn<bool>("selectWords");
1212 1213
    QTest::addColumn<bool>("focus");
    QTest::addColumn<bool>("focusOnPress");
1214 1215

    // import installed
1216 1217 1218 1219 1220 1221 1222 1223 1224
    QTest::newRow("SelectWords focused") << testFile("mouseselectionmode_words.qml") << true << true << true;
    QTest::newRow("SelectCharacters focused") << testFile("mouseselectionmode_characters.qml") << false << true << true;
    QTest::newRow("default focused") << testFile("mouseselectionmode_default.qml") << false << true << true;
    QTest::newRow("SelectWords unfocused") << testFile("mouseselectionmode_words.qml") << true << false << false;
    QTest::newRow("SelectCharacters unfocused") << testFile("mouseselectionmode_characters.qml") << false << false << false;
    QTest::newRow("default unfocused") << testFile("mouseselectionmode_default.qml") << false << true << false;
    QTest::newRow("SelectWords focuss on press") << testFile("mouseselectionmode_words.qml") << true << false << true;
    QTest::newRow("SelectCharacters focus on press") << testFile("mouseselectionmode_characters.qml") << false << false << true;
    QTest::newRow("default focus on press") << testFile("mouseselectionmode_default.qml") << false << false << true;
1225 1226
}

1227
void tst_qquicktextinput::mouseSelectionMode()
1228 1229 1230
{
    QFETCH(QString, qmlfile);
    QFETCH(bool, selectWords);
1231 1232
    QFETCH(bool, focus);
    QFETCH(bool, focusOnPress);
1233 1234 1235

    QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

1236
    QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1237 1238

    canvas.show();
1239
    canvas.requestActivateWindow();
1240
    QTest::qWaitForWindowShown(&canvas);
1241
    QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1242 1243

    QVERIFY(canvas.rootObject() != 0);
1244
    QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1245 1246
    QVERIFY(textInputObject != 0);

1247 1248 1249
    textInputObject->setFocus(focus);
    textInputObject->setFocusOnPress(focusOnPress);

1250 1251 1252 1253 1254
    // press-and-drag-and-release from x1 to x2
    int x1 = 10;
    int x2 = 70;
    int y = textInputObject->height()/2;
    QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1255
    QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1256
    QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1257
    QTest::qWait(300);
1258
    if (selectWords) {
1259
        QTRY_COMPARE(textInputObject->selectedText(), text);
1260
    } else {
1261 1262
        QTRY_VERIFY(textInputObject->selectedText().length() > 3);
        QVERIFY(textInputObject->selectedText() != text);
1263 1264 1265
    }
}

1266
void tst_qquicktextinput::horizontalAlignment_data()
1267 1268 1269 1270 1271 1272 1273 1274 1275
{
    QTest::addColumn<int>("hAlign");
    QTest::addColumn<QString>("expectfile");

    QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
    QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
    QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
}

1276
void tst_qquicktextinput::horizontalAlignment()
1277
{
1278
    QSKIP("Image comparison of text is almost guaranteed to fail during development");
1279

1280 1281 1282
    QFETCH(int, hAlign);
    QFETCH(QString, expectfile);

1283
    QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1284 1285

    canvas.show();
1286
    canvas.requestActivateWindow();
1287
    QTest::qWaitForWindowShown(&canvas);
1288
    QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1289 1290 1291
    QObject *ob = canvas.rootObject();
    QVERIFY(ob != 0);
    ob->setProperty("horizontalAlignment",hAlign);
Aaron Kennedy's avatar
Aaron Kennedy committed
1292
    QImage actual = canvas.grabFrameBuffer();
1293 1294 1295 1296 1297 1298 1299 1300

    expectfile = createExpectedFileIfNotFound(expectfile, actual);

    QImage expect(expectfile);

    QCOMPARE(actual,expect);
}

1301
void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1302
{