diff options
Diffstat (limited to 'tests/auto/declarative/qquicktextedit')
42 files changed, 2851 insertions, 0 deletions
diff --git a/tests/auto/declarative/qquicktextedit/data/CursorRect.qml b/tests/auto/declarative/qquicktextedit/data/CursorRect.qml new file mode 100644 index 0000000000..cae3e63b72 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/CursorRect.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + objectName: "myEdit" + width: 50 + text: "This is a long piece of text" +} diff --git a/tests/auto/declarative/qquicktextedit/data/alignments.qml b/tests/auto/declarative/qquicktextedit/data/alignments.qml new file mode 100644 index 0000000000..7d365da8cb --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments.qml @@ -0,0 +1,41 @@ +import QtQuick 2.0 + +Rectangle { + id: top + width: 70; height: 70; + + property alias horizontalAlignment: t.horizontalAlignment + property alias verticalAlignment: t.verticalAlignment + property alias wrapMode: t.wrapMode + property alias running: timer.running + property string txt: "Test" + + Rectangle { + anchors.centerIn: parent + width: 40 + height: 40 + color: "green" + + TextEdit { + id: t + + anchors.fill: parent + horizontalAlignment: TextEdit.AlignRight + verticalAlignment: TextEdit.AlignBottom + wrapMode: TextEdit.WordWrap + text: top.txt + } + Timer { + id: timer + + interval: 1 + running: true + repeat: true + onTriggered: { + top.txt = top.txt + "<br>more " + top.txt.length; + if (top.txt.length > 50) + running = false + } + } + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/alignments_cb.png b/tests/auto/declarative/qquicktextedit/data/alignments_cb.png Binary files differnew file mode 100644 index 0000000000..99de2192de --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments_cb.png diff --git a/tests/auto/declarative/qquicktextedit/data/alignments_cc.png b/tests/auto/declarative/qquicktextedit/data/alignments_cc.png Binary files differnew file mode 100644 index 0000000000..cb85251180 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments_cc.png diff --git a/tests/auto/declarative/qquicktextedit/data/alignments_ct.png b/tests/auto/declarative/qquicktextedit/data/alignments_ct.png Binary files differnew file mode 100644 index 0000000000..ddca549c82 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments_ct.png diff --git a/tests/auto/declarative/qquicktextedit/data/alignments_lb.png b/tests/auto/declarative/qquicktextedit/data/alignments_lb.png Binary files differnew file mode 100644 index 0000000000..1b50a81f3d --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments_lb.png diff --git a/tests/auto/declarative/qquicktextedit/data/alignments_lc.png b/tests/auto/declarative/qquicktextedit/data/alignments_lc.png Binary files differnew file mode 100644 index 0000000000..f041b868f8 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments_lc.png diff --git a/tests/auto/declarative/qquicktextedit/data/alignments_lt.png b/tests/auto/declarative/qquicktextedit/data/alignments_lt.png Binary files differnew file mode 100644 index 0000000000..c75e0d158e --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments_lt.png diff --git a/tests/auto/declarative/qquicktextedit/data/alignments_rb.png b/tests/auto/declarative/qquicktextedit/data/alignments_rb.png Binary files differnew file mode 100644 index 0000000000..b06a5da715 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments_rb.png diff --git a/tests/auto/declarative/qquicktextedit/data/alignments_rc.png b/tests/auto/declarative/qquicktextedit/data/alignments_rc.png Binary files differnew file mode 100644 index 0000000000..e468857cd0 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments_rc.png diff --git a/tests/auto/declarative/qquicktextedit/data/alignments_rt.png b/tests/auto/declarative/qquicktextedit/data/alignments_rt.png Binary files differnew file mode 100644 index 0000000000..576715ffce --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/alignments_rt.png diff --git a/tests/auto/declarative/qquicktextedit/data/cursorTest.qml b/tests/auto/declarative/qquicktextedit/data/cursorTest.qml new file mode 100644 index 0000000000..7bfc869403 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/cursorTest.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Rectangle { width: 300; height: 300; color: "white" + property string contextualProperty: "Hello" + TextEdit { text: "Hello world!"; id: textEditObject; objectName: "textEditObject" + resources: [ Component { id:cursor; Item { id:cursorInstance; objectName: "cursorInstance"; property string localProperty: contextualProperty } } ] + cursorDelegate: cursor + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/cursorVisible.qml b/tests/auto/declarative/qquicktextedit/data/cursorVisible.qml new file mode 100644 index 0000000000..49e9386947 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/cursorVisible.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +Item { + width: 100 + height: 20 +} diff --git a/tests/auto/declarative/qquicktextedit/data/geometrySignals.qml b/tests/auto/declarative/qquicktextedit/data/geometrySignals.qml new file mode 100644 index 0000000000..3dbe61c74b --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/geometrySignals.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + width: 400; height: 500; + property int bindingWidth: text.width + property int bindingHeight: text.height + + TextInput { + id: text + anchors.fill: parent + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/horizontalAlignment_RightToLeft.qml b/tests/auto/declarative/qquicktextedit/data/horizontalAlignment_RightToLeft.qml new file mode 100644 index 0000000000..4cd92367ec --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/horizontalAlignment_RightToLeft.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 + +Rectangle { + id: top + width: 200; height: 70; + + property alias horizontalAlignment: text.horizontalAlignment + property string text: "اختبا" + + Rectangle { + anchors.centerIn: parent + width: 200 + height: 20 + color: "green" + + TextEdit { + id: text + objectName: "text" + anchors.fill: parent + text: top.text + focus: true + } + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/http/ErrItem.qml b/tests/auto/declarative/qquicktextedit/data/http/ErrItem.qml new file mode 100644 index 0000000000..68c0e0c093 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/http/ErrItem.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +Item{ + Fungus{ + palatable: false; + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/http/NormItem.qml b/tests/auto/declarative/qquicktextedit/data/http/NormItem.qml new file mode 100644 index 0000000000..2e4c1ed440 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/http/NormItem.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +Item { + objectName: "delegateOkay" + Rectangle { } +} diff --git a/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTest.qml b/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTest.qml new file mode 100644 index 0000000000..be4526e22b --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTest.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 + +Rectangle { width: 300; height: 300; color: "white" + resources: [ + Component { id:cursorFail; FailItem { objectName: "delegateFail" } }, + Component { id:cursorWait; WaitItem { objectName: "delegateSlow" } }, + Component { id:cursorNorm; NormItem { objectName: "delegateOkay" } }, + Component { id:cursorErr; ErrItem { objectName: "delegateErrorA" } } + ] + TextEdit { + cursorDelegate: cursorFail + } + TextEdit { + cursorDelegate: cursorWait + } + TextEdit { + cursorDelegate: cursorNorm + } + TextEdit { + cursorDelegate: cursorErr + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTestFail1.qml b/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTestFail1.qml new file mode 100644 index 0000000000..1d7763f913 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTestFail1.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 + +Rectangle { width: 300; height: 300; color: "white" + resources: [ + Component { id:cursorFail; FailItem { objectName: "delegateFail" } }, + Component { id:cursorWait; WaitItem { objectName: "delegateSlow" } }, + Component { id:cursorNorm; NormItem { objectName: "delegateOkay" } } + ] + TextEdit { + cursorDelegate: cursorFail + } + TextEdit { + cursorDelegate: cursorWait + } + TextEdit { + cursorDelegate: cursorNorm + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTestFail2.qml b/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTestFail2.qml new file mode 100644 index 0000000000..c82ec02e68 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTestFail2.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 + +Rectangle { width: 300; height: 300; color: "white" + resources: [ + Component { id:cursorWait; WaitItem { objectName: "delegateSlow" } }, + Component { id:cursorNorm; NormItem { objectName: "delegateOkay" } }, + Component { id:cursorErr; ErrItem { objectName: "delegateErrorA" } } + ] + TextEdit { + cursorDelegate: cursorWait + } + TextEdit { + cursorDelegate: cursorNorm + } + TextEdit { + cursorDelegate: cursorErr + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTestPass.qml b/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTestPass.qml new file mode 100644 index 0000000000..96d582c95d --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/http/cursorHttpTestPass.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 + +Rectangle { width: 300; height: 300; color: "white" + resources: [ + Component { id:cursorWait; WaitItem { objectName: "delegateSlow" } }, + Component { id:cursorNorm; NormItem { objectName: "delegateOkay" } } + ] + TextEdit { + cursorDelegate: cursorWait + text: "Hello" + } + TextEdit { + objectName: "textEditObject" + cursorDelegate: cursorNorm + focus: true; + text: "Hello" + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/http/qmldir b/tests/auto/declarative/qquicktextedit/data/http/qmldir new file mode 100644 index 0000000000..886e6ffec0 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/http/qmldir @@ -0,0 +1,4 @@ +ErrItem ErrItem.qml +NormItem NormItem.qml +FailItem FailItem.qml +WaitItem WaitItem.qml diff --git a/tests/auto/declarative/qquicktextedit/data/httpfail/FailItem.qml b/tests/auto/declarative/qquicktextedit/data/httpfail/FailItem.qml new file mode 100644 index 0000000000..8161843479 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/httpfail/FailItem.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + Rectangle { } +} diff --git a/tests/auto/declarative/qquicktextedit/data/httpslow/WaitItem.qml b/tests/auto/declarative/qquicktextedit/data/httpslow/WaitItem.qml new file mode 100644 index 0000000000..8161843479 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/httpslow/WaitItem.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + Rectangle { } +} diff --git a/tests/auto/declarative/qquicktextedit/data/inputContext.qml b/tests/auto/declarative/qquicktextedit/data/inputContext.qml new file mode 100644 index 0000000000..a37c77e3bf --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/inputContext.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +TextEdit { + width: 200 + text: "supercalifra" + focus: true +} diff --git a/tests/auto/declarative/qquicktextedit/data/inputMethodEvent.qml b/tests/auto/declarative/qquicktextedit/data/inputMethodEvent.qml new file mode 100644 index 0000000000..e3f629ce3e --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/inputMethodEvent.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +TextEdit { + focus: true +} diff --git a/tests/auto/declarative/qquicktextedit/data/inputmethodhints.qml b/tests/auto/declarative/qquicktextedit/data/inputmethodhints.qml new file mode 100644 index 0000000000..dec3b978e7 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/inputmethodhints.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +TextEdit { + text: "Hello world!" + inputMethodHints: Qt.ImhNoPredictiveText +} diff --git a/tests/auto/declarative/qquicktextedit/data/mouseselection_default.qml b/tests/auto/declarative/qquicktextedit/data/mouseselection_default.qml new file mode 100644 index 0000000000..ac32f4ced7 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/mouseselection_default.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + selectByMouse: false +} diff --git a/tests/auto/declarative/qquicktextedit/data/mouseselection_false.qml b/tests/auto/declarative/qquicktextedit/data/mouseselection_false.qml new file mode 100644 index 0000000000..ac32f4ced7 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/mouseselection_false.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + selectByMouse: false +} diff --git a/tests/auto/declarative/qquicktextedit/data/mouseselection_false_words.qml b/tests/auto/declarative/qquicktextedit/data/mouseselection_false_words.qml new file mode 100644 index 0000000000..86aea46a85 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/mouseselection_false_words.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + text: "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" + selectByMouse: false + mouseSelectionMode: TextEdit.SelectWords +} diff --git a/tests/auto/declarative/qquicktextedit/data/mouseselection_true.qml b/tests/auto/declarative/qquicktextedit/data/mouseselection_true.qml new file mode 100644 index 0000000000..7c7cb0b6fc --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/mouseselection_true.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + selectByMouse: true +} diff --git a/tests/auto/declarative/qquicktextedit/data/mouseselection_true_words.qml b/tests/auto/declarative/qquicktextedit/data/mouseselection_true_words.qml new file mode 100644 index 0000000000..c356999220 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/mouseselection_true_words.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + text: "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" + selectByMouse: true + mouseSelectionMode: TextEdit.SelectWords +} diff --git a/tests/auto/declarative/qquicktextedit/data/mouseselectionmode_characters.qml b/tests/auto/declarative/qquicktextedit/data/mouseselectionmode_characters.qml new file mode 100644 index 0000000000..c1fe42fd57 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/mouseselectionmode_characters.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + selectByMouse: true + mouseSelectionMode: TextEdit.SelectCharacters +} diff --git a/tests/auto/declarative/qquicktextedit/data/mouseselectionmode_default.qml b/tests/auto/declarative/qquicktextedit/data/mouseselectionmode_default.qml new file mode 100644 index 0000000000..7c7cb0b6fc --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/mouseselectionmode_default.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + selectByMouse: true +} diff --git a/tests/auto/declarative/qquicktextedit/data/mouseselectionmode_words.qml b/tests/auto/declarative/qquicktextedit/data/mouseselectionmode_words.qml new file mode 100644 index 0000000000..0a372bbf83 --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/mouseselectionmode_words.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + selectByMouse: true + mouseSelectionMode: TextEdit.SelectWords +} diff --git a/tests/auto/declarative/qquicktextedit/data/navigation.qml b/tests/auto/declarative/qquicktextedit/data/navigation.qml new file mode 100644 index 0000000000..0201c62b3c --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/navigation.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 + +Rectangle { + property variant myInput: input + + width: 800; height: 600; color: "blue" + + Item { + id: firstItem; + KeyNavigation.right: input + } + + TextEdit { id: input; focus: true + KeyNavigation.left: firstItem + KeyNavigation.right: lastItem + KeyNavigation.up: firstItem + KeyNavigation.down: lastItem + text: "a" + } + Item { + id: lastItem + KeyNavigation.left: input + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/openInputPanel.qml b/tests/auto/declarative/qquicktextedit/data/openInputPanel.qml new file mode 100644 index 0000000000..d3aecf21be --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/openInputPanel.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +TextEdit { + width: 100; height: 100 + text: "Hello world" + focus: false +} diff --git a/tests/auto/declarative/qquicktextedit/data/positionAt.qml b/tests/auto/declarative/qquicktextedit/data/positionAt.qml new file mode 100644 index 0000000000..19093281fe --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/positionAt.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +TextEdit { + focus: true + objectName: "myInput" + width: 50 + height: 25 + text: "This is\n a long piece of text" +} diff --git a/tests/auto/declarative/qquicktextedit/data/qtbug-22058.qml b/tests/auto/declarative/qquicktextedit/data/qtbug-22058.qml new file mode 100644 index 0000000000..8ad1514fbf --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/qtbug-22058.qml @@ -0,0 +1,39 @@ +import QtQuick 2.0 + +Rectangle { + property variant inputField: textedit + height: 200 + width: 200 + + Rectangle { + height: parent.height /2 + width: parent.width + color: "transparent" + border.color: "black" + border.width: 4 + Text { + anchors.centerIn: parent + height: parent.height * .95 + width: parent.width * .95 + text: textedit.text + } + } + + Rectangle { + height: parent.height /2 + width: parent.width + color: "transparent" + border.color: "black" + border.width: 4 + anchors.bottom: parent.bottom + TextEdit { + id: textedit + anchors.centerIn: parent + height: parent.height * .95 + width: parent.width * .95 + focus: true + wrapMode: TextEdit.WordWrap + text: "" + } + } +} diff --git a/tests/auto/declarative/qquicktextedit/data/readOnly.qml b/tests/auto/declarative/qquicktextedit/data/readOnly.qml new file mode 100644 index 0000000000..085adba5fb --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/data/readOnly.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Rectangle { + property variant myInput: input + + width: 800; height: 600; color: "blue" + + TextEdit { id: input; focus: true + readOnly: true + text: "I am the very model of a modern major general.\n" + } +} diff --git a/tests/auto/declarative/qquicktextedit/qquicktextedit.pro b/tests/auto/declarative/qquicktextedit/qquicktextedit.pro new file mode 100644 index 0000000000..ae2233dafc --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/qquicktextedit.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qquicktextedit +macx:CONFIG -= app_bundle + +SOURCES += tst_qquicktextedit.cpp ../shared/testhttpserver.cpp +HEADERS += ../shared/testhttpserver.h + +testDataFiles.files = data +testDataFiles.path = . +DEPLOYMENT += testDataFiles + +QT += core-private gui-private v8-private declarative-private opengl-private network widgets-private testlib diff --git a/tests/auto/declarative/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/declarative/qquicktextedit/tst_qquicktextedit.cpp new file mode 100644 index 0000000000..a0d36d6e9d --- /dev/null +++ b/tests/auto/declarative/qquicktextedit/tst_qquicktextedit.cpp @@ -0,0 +1,2467 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** 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. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <QtTest/QSignalSpy> +#include "../shared/testhttpserver.h" +#include <math.h> +#include <QFile> +#include <QTextDocument> +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/qdeclarativeexpression.h> +#include <QtDeclarative/qdeclarativecomponent.h> +#include <QtGui/qguiapplication.h> +#include <private/qquicktextedit_p.h> +#include <private/qquicktextedit_p_p.h> +#include <private/qsgdistancefieldglyphcache_p.h> +#include <QFontMetrics> +#include <QQuickView> +#include <QDir> +#include <QStyle> +#include <QInputContext> +#include <QInputPanel> +#include <QClipboard> +#include <QMimeData> +#include <private/qtextcontrol_p.h> +#include "../shared/util.h" + +#ifdef Q_WS_MAC +#include <Carbon/Carbon.h> +#endif + +#define QTBUG_21691 +#define QTBUG_21691_MESSAGE "QTBUG-21691: The test needs to be rewritten to not use QInputContext" + +Q_DECLARE_METATYPE(QQuickTextEdit::SelectionMode) +DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) + +QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual) +{ + // XXX This will be replaced by some clever persistent platform image store. + QString persistent_dir = TESTDATA(""); + 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; +} + + +class tst_qquicktextedit : public QObject + +{ + Q_OBJECT +public: + tst_qquicktextedit(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + void text(); + void width(); + void wrap(); + void textFormat(); + void alignments(); + void alignments_data(); + + // ### these tests may be trivial + void hAlign(); + void hAlign_RightToLeft(); + void vAlign(); + void font(); + void color(); + void textMargin(); + void persistentSelection(); + void focusOnPress(); + void selection(); + void isRightToLeft_data(); + void isRightToLeft(); + void keySelection(); + void moveCursorSelection_data(); + void moveCursorSelection(); + void moveCursorSelectionSequence_data(); + void moveCursorSelectionSequence(); + void mouseSelection_data(); + void mouseSelection(); + void mouseSelectionMode_data(); + void mouseSelectionMode(); + void dragMouseSelection(); + void inputMethodHints(); + + void positionAt(); + + void cursorDelegate(); + void cursorVisible(); + void delegateLoading_data(); + void delegateLoading(); + void navigation(); + void readOnly(); + void copyAndPaste(); + void canPaste(); + void canPasteEmpty(); + void textInput(); + void openInputPanel(); + void geometrySignals(); + void pastingRichText_QTBUG_14003(); + void implicitSize_data(); + void implicitSize(); + void testQtQuick11Attributes(); + void testQtQuick11Attributes_data(); + + void preeditMicroFocus(); + void inputContextMouseHandler(); + void inputMethodComposing(); + void cursorRectangleSize(); + + void emptytags_QTBUG_22058(); + +private: + void simulateKey(QQuickView *, int key, Qt::KeyboardModifiers modifiers = 0); + + QStringList standard; + QStringList richText; + + QStringList hAlignmentStrings; + QStringList vAlignmentStrings; + + QList<Qt::Alignment> vAlignments; + QList<Qt::Alignment> hAlignments; + + QStringList colorStrings; + + QDeclarativeEngine engine; +}; +void tst_qquicktextedit::initTestCase() +{ +} + +void tst_qquicktextedit::cleanupTestCase() +{ + +} +tst_qquicktextedit::tst_qquicktextedit() +{ + standard << "the quick brown fox jumped over the lazy dog" + << "the quick brown fox\n jumped over the lazy dog" + << "Hello, world!" + << "!dlrow ,olleH"; + + richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>" + << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>"; + + hAlignmentStrings << "AlignLeft" + << "AlignRight" + << "AlignHCenter"; + + vAlignmentStrings << "AlignTop" + << "AlignBottom" + << "AlignVCenter"; + + hAlignments << Qt::AlignLeft + << Qt::AlignRight + << Qt::AlignHCenter; + + vAlignments << Qt::AlignTop + << Qt::AlignBottom + << Qt::AlignVCenter; + + colorStrings << "aliceblue" + << "antiquewhite" + << "aqua" + << "darkkhaki" + << "darkolivegreen" + << "dimgray" + << "palevioletred" + << "lightsteelblue" + << "#000000" + << "#AAAAAA" + << "#FFFFFF" + << "#2AC05F"; + // + // need a different test to do alpha channel test + // << "#AA0011DD" + // << "#00F16B11"; + // +} + +void tst_qquicktextedit::text() +{ + { + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->text(), QString("")); + } + + for (int i = 0; i < standard.size(); i++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->text(), standard.at(i)); + } + + for (int i = 0; i < richText.size(); i++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QString actual = textEditObject->text(); + QString expected = richText.at(i); + actual.replace(QRegExp(".*<body[^>]*>"),""); + actual.replace(QRegExp("(<[^>]*>)+"),"<>"); + expected.replace(QRegExp("(<[^>]*>)+"),"<>"); + QCOMPARE(actual.simplified(),expected.simplified()); + } +} + +void tst_qquicktextedit::width() +{ + // uses Font metrics to find the width for standard and document to find the width for rich + { + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->width(), 0.0); + } + + bool requiresUnhintedMetrics = !qmlDisableDistanceField(); + + for (int i = 0; i < standard.size(); i++) + { + QFont f; + qreal metricWidth = 0.0; + + if (requiresUnhintedMetrics) { + QString s = standard.at(i); + s.replace(QLatin1Char('\n'), QChar::LineSeparator); + + QTextLayout layout(s); + layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic); + { + QTextOption option; + option.setUseDesignMetrics(true); + layout.setTextOption(option); + } + + layout.beginLayout(); + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + } + + layout.endLayout(); + + metricWidth = ceil(layout.boundingRect().width()); + } else { + QFontMetricsF fm(f); + metricWidth = fm.size(Qt::TextExpandTabs | Qt::TextShowMnemonic, standard.at(i)).width(); + metricWidth = ceil(metricWidth); + } + + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->width(), qreal(metricWidth)); + } + + for (int i = 0; i < richText.size(); i++) + { + QTextDocument document; + document.setHtml(richText.at(i)); + document.setDocumentMargin(0); + if (requiresUnhintedMetrics) + document.setUseDesignMetrics(true); + + int documentWidth = ceil(document.idealWidth()); + + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->width(), qreal(documentWidth)); + } +} + +void tst_qquicktextedit::wrap() +{ + // for specified width and wrap set true + { + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\"; wrapMode: TextEdit.WordWrap; width: 300 }", QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->width(), 300.); + } + + for (int i = 0; i < standard.size(); i++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + standard.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->width(), 300.); + } + + for (int i = 0; i < richText.size(); i++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + richText.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->width(), 300.); + } + +} + +void tst_qquicktextedit::textFormat() +{ + { + QDeclarativeComponent textComponent(&engine); + textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile("")); + QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create()); + + QVERIFY(textObject != 0); + QVERIFY(textObject->textFormat() == QQuickTextEdit::RichText); + } + { + QDeclarativeComponent textComponent(&engine); + textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile("")); + QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create()); + + QVERIFY(textObject != 0); + QVERIFY(textObject->textFormat() == QQuickTextEdit::PlainText); + } +} + +void tst_qquicktextedit::alignments_data() +{ + QTest::addColumn<int>("hAlign"); + QTest::addColumn<int>("vAlign"); + QTest::addColumn<QString>("expectfile"); + + QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << "alignments_lt"; + QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << "alignments_rt"; + QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << "alignments_ct"; + + QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << "alignments_lb"; + QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << "alignments_rb"; + QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << "alignments_cb"; + + QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << "alignments_lc"; + QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << "alignments_rc"; + QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << "alignments_cc"; +} + + +void tst_qquicktextedit::alignments() +{ + QSKIP("Image comparison of text is almost guaranteed to fail during development"); + + QFETCH(int, hAlign); + QFETCH(int, vAlign); + QFETCH(QString, expectfile); + + QQuickView canvas(QUrl::fromLocalFile(TESTDATA("alignments.qml"))); + + canvas.show(); + canvas.requestActivateWindow(); + QTest::qWaitForWindowShown(&canvas); + QTRY_COMPARE(&canvas, qGuiApp->focusWindow()); + + QObject *ob = canvas.rootObject(); + QVERIFY(ob != 0); + ob->setProperty("horizontalAlignment",hAlign); + ob->setProperty("verticalAlignment",vAlign); + QTRY_COMPARE(ob->property("running").toBool(),false); + QImage actual = canvas.grabFrameBuffer(); + + expectfile = createExpectedFileIfNotFound(expectfile, actual); + + QImage expect(expectfile); + + QCOMPARE(actual,expect); +} + + +//the alignment tests may be trivial o.oa +void tst_qquicktextedit::hAlign() +{ + //test one align each, and then test if two align fails. + + for (int i = 0; i < standard.size(); i++) + { + for (int j=0; j < hAlignmentStrings.size(); j++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j)); + } + } + + for (int i = 0; i < richText.size(); i++) + { + for (int j=0; j < hAlignmentStrings.size(); j++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j)); + } + } + +} + +void tst_qquicktextedit::hAlign_RightToLeft() +{ + QQuickView canvas(QUrl::fromLocalFile(TESTDATA("horizontalAlignment_RightToLeft.qml"))); + QQuickTextEdit *textEdit = canvas.rootObject()->findChild<QQuickTextEdit*>("text"); + QVERIFY(textEdit != 0); + canvas.show(); + + const QString rtlText = textEdit->text(); + + // implicit alignment should follow the reading direction of text + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); + + // explicitly left aligned + textEdit->setHAlign(QQuickTextEdit::AlignLeft); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft); + QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2); + + // explicitly right aligned + textEdit->setHAlign(QQuickTextEdit::AlignRight); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); + + QString textString = textEdit->text(); + textEdit->setText(QString("<i>") + textString + QString("</i>")); + textEdit->resetHAlign(); + + // implicitly aligned rich text should follow the reading direction of RTL text + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight); + QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign()); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); + + // explicitly left aligned rich text + textEdit->setHAlign(QQuickTextEdit::AlignLeft); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft); + QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign()); + QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2); + + // explicitly right aligned rich text + textEdit->setHAlign(QQuickTextEdit::AlignRight); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight); + QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign()); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); + + textEdit->setText(textString); + + // explicitly center aligned + textEdit->setHAlign(QQuickTextEdit::AlignHCenter); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignHCenter); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); + + // reseted alignment should go back to following the text reading direction + textEdit->resetHAlign(); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); + + // mirror the text item + QQuickItemPrivate::get(textEdit)->setLayoutMirror(true); + + // mirrored implicit alignment should continue to follow the reading direction of the text + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight); + QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); + + // mirrored explicitly right aligned behaves as left aligned + textEdit->setHAlign(QQuickTextEdit::AlignRight); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight); + QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignLeft); + QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2); + + // mirrored explicitly left aligned behaves as right aligned + textEdit->setHAlign(QQuickTextEdit::AlignLeft); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft); + QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); + + // disable mirroring + QQuickItemPrivate::get(textEdit)->setLayoutMirror(false); + textEdit->resetHAlign(); + + // English text should be implicitly left aligned + textEdit->setText("Hello world!"); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft); + QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2); + + canvas.requestActivateWindow(); + QTest::qWaitForWindowShown(&canvas); + QTRY_COMPARE(&canvas, qGuiApp->focusWindow()); + + textEdit->setText(QString()); + { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(&canvas, &ev); } + QEXPECT_FAIL("", "QTBUG-21691", Abort); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight); + { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(&canvas, &ev); } + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft); + +#ifndef Q_OS_MAC // QTBUG-18040 + // empty text with implicit alignment follows the system locale-based + // keyboard input direction from QGuiApplication::keyboardInputDirection + textEdit->setText(""); + QCOMPARE(textEdit->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ? + QQuickTextEdit::AlignLeft : QQuickTextEdit::AlignRight); + if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight) + QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2); + else + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); + textEdit->setHAlign(QQuickTextEdit::AlignRight); + QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); +#endif + +#ifndef Q_OS_MAC // QTBUG-18040 + // alignment of TextEdit with no text set to it + QString componentStr = "import QtQuick 2.0\nTextEdit {}"; + QDeclarativeComponent textComponent(&engine); + textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create()); + QCOMPARE(textObject->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ? + QQuickTextEdit::AlignLeft : QQuickTextEdit::AlignRight); + delete textObject; +#endif +} + +void tst_qquicktextedit::vAlign() +{ + //test one align each, and then test if two align fails. + + for (int i = 0; i < standard.size(); i++) + { + for (int j=0; j < vAlignmentStrings.size(); j++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j)); + } + } + + for (int i = 0; i < richText.size(); i++) + { + for (int j=0; j < vAlignmentStrings.size(); j++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j)); + } + } + +} + +void tst_qquicktextedit::font() +{ + //test size, then bold, then italic, then family + { + QString componentStr = "import QtQuick 2.0\nTextEdit { font.pointSize: 40; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->font().pointSize(), 40); + QCOMPARE(textEditObject->font().bold(), false); + QCOMPARE(textEditObject->font().italic(), false); + } + + { + QString componentStr = "import QtQuick 2.0\nTextEdit { font.bold: true; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->font().bold(), true); + QCOMPARE(textEditObject->font().italic(), false); + } + + { + QString componentStr = "import QtQuick 2.0\nTextEdit { font.italic: true; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->font().italic(), true); + QCOMPARE(textEditObject->font().bold(), false); + } + + { + QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"Helvetica\"; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->font().family(), QString("Helvetica")); + QCOMPARE(textEditObject->font().bold(), false); + QCOMPARE(textEditObject->font().italic(), false); + } + + { + QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"\"; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->font().family(), QString("")); + } +} + +void tst_qquicktextedit::color() +{ + //test initial color + { + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QQuickTextEditPrivate *textEditPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(textEditObject)); + + QVERIFY(textEditObject); + QVERIFY(textEditPrivate); + QVERIFY(textEditPrivate->control); + + QPalette pal = textEditPrivate->control->palette(); + QCOMPARE(textEditPrivate->color, QColor("black")); + QCOMPARE(textEditPrivate->color, pal.color(QPalette::Text)); + } + //test normal + for (int i = 0; i < colorStrings.size(); i++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + //qDebug() << "textEditObject: " << textEditObject->color() << "vs. " << QColor(colorStrings.at(i)); + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->color(), QColor(colorStrings.at(i))); + } + + //test selection + for (int i = 0; i < colorStrings.size(); i++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->selectionColor(), QColor(colorStrings.at(i))); + } + + //test selected text + for (int i = 0; i < colorStrings.size(); i++) + { + QString componentStr = "import QtQuick 2.0\nTextEdit { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->selectedTextColor(), QColor(colorStrings.at(i))); + } + + { + QString colorStr = "#AA001234"; + QColor testColor("#001234"); + testColor.setAlpha(170); + + QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStr + "\"; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->color(), testColor); + } +} + +void tst_qquicktextedit::textMargin() +{ + for (qreal i=0; i<=10; i+=0.3) { + QString componentStr = "import QtQuick 2.0\nTextEdit { textMargin: " + QString::number(i) + "; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->textMargin(), i); + } +} + +void tst_qquicktextedit::persistentSelection() +{ + { + QString componentStr = "import QtQuick 2.0\nTextEdit { persistentSelection: true; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->persistentSelection(), true); + } + + { + QString componentStr = "import QtQuick 2.0\nTextEdit { persistentSelection: false; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->persistentSelection(), false); + } +} + +void tst_qquicktextedit::focusOnPress() +{ + { + QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: true; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->focusOnPress(), true); + } + + { + QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: false; text: \"Hello World\" }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + QVERIFY(textEditObject != 0); + QCOMPARE(textEditObject->focusOnPress(), false); + } +} + +void tst_qquicktextedit::selection() +{ + QString testStr = standard[0];//TODO: What should happen for multiline/rich text? + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + QVERIFY(textEditObject != 0); + + + //Test selection follows cursor + for (int i=0; i<= testStr.size(); i++) { + textEditObject->setCursorPosition(i); + QCOMPARE(textEditObject->cursorPosition(), i); + QCOMPARE(textEditObject->selectionStart(), i); + QCOMPARE(textEditObject->selectionEnd(), i); + QVERIFY(textEditObject->selectedText().isNull()); + } + + textEditObject->setCursorPosition(0); + QVERIFY(textEditObject->cursorPosition() == 0); + QVERIFY(textEditObject->selectionStart() == 0); + QVERIFY(textEditObject->selectionEnd() == 0); + QVERIFY(textEditObject->selectedText().isNull()); + + // Verify invalid positions are ignored. + textEditObject->setCursorPosition(-1); + QVERIFY(textEditObject->cursorPosition() == 0); + QVERIFY(textEditObject->selectionStart() == 0); + QVERIFY(textEditObject->selectionEnd() == 0); + QVERIFY(textEditObject->selectedText().isNull()); + + textEditObject->setCursorPosition(textEditObject->text().count()+1); + QVERIFY(textEditObject->cursorPosition() == 0); + QVERIFY(textEditObject->selectionStart() == 0); + QVERIFY(textEditObject->selectionEnd() == 0); + QVERIFY(textEditObject->selectedText().isNull()); + + //Test selection + for (int i=0; i<= testStr.size(); i++) { + textEditObject->select(0,i); + QCOMPARE(testStr.mid(0,i), textEditObject->selectedText()); + } + for (int i=0; i<= testStr.size(); i++) { + textEditObject->select(i,testStr.size()); + QCOMPARE(testStr.mid(i,testStr.size()-i), textEditObject->selectedText()); + } + + textEditObject->setCursorPosition(0); + QVERIFY(textEditObject->cursorPosition() == 0); + QVERIFY(textEditObject->selectionStart() == 0); + QVERIFY(textEditObject->selectionEnd() == 0); + QVERIFY(textEditObject->selectedText().isNull()); + + //Test Error Ignoring behaviour + textEditObject->setCursorPosition(0); + QVERIFY(textEditObject->selectedText().isNull()); + textEditObject->select(-10,0); + QVERIFY(textEditObject->selectedText().isNull()); + textEditObject->select(100,101); + QVERIFY(textEditObject->selectedText().isNull()); + textEditObject->select(0,-10); + QVERIFY(textEditObject->selectedText().isNull()); + textEditObject->select(0,100); + QVERIFY(textEditObject->selectedText().isNull()); + textEditObject->select(0,10); + QVERIFY(textEditObject->selectedText().size() == 10); + textEditObject->select(-10,0); + QVERIFY(textEditObject->selectedText().size() == 10); + textEditObject->select(100,101); + QVERIFY(textEditObject->selectedText().size() == 10); + textEditObject->select(0,-10); + QVERIFY(textEditObject->selectedText().size() == 10); + textEditObject->select(0,100); + QVERIFY(textEditObject->selectedText().size() == 10); + + textEditObject->deselect(); + QVERIFY(textEditObject->selectedText().isNull()); + textEditObject->select(0,10); + QVERIFY(textEditObject->selectedText().size() == 10); + textEditObject->deselect(); + QVERIFY(textEditObject->selectedText().isNull()); +} + +void tst_qquicktextedit::isRightToLeft_data() +{ + 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; +} + +void tst_qquicktextedit::isRightToLeft() +{ + QFETCH(QString, text); + QFETCH(bool, emptyString); + QFETCH(bool, firstCharacter); + QFETCH(bool, lastCharacter); + QFETCH(bool, middleCharacter); + QFETCH(bool, startString); + QFETCH(bool, midString); + QFETCH(bool, endString); + + QQuickTextEdit textEdit; + textEdit.setText(text); + + // first test that the right string is delivered to the QString::isRightToLeft() + QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft()); + QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft()); + QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft()); + QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft()); + QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft()); + QCOMPARE(textEdit.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 TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start."); + QCOMPARE(textEdit.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(textEdit.isRightToLeft(0,0), emptyString); + QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter); + QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter); + QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter); + QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString); + QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString); + if (text.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start."); + QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString); +} + +void tst_qquicktextedit::keySelection() +{ + QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml"))); + canvas.show(); + canvas.requestActivateWindow(); + QTest::qWaitForWindowShown(&canvas); + QTRY_COMPARE(&canvas, qGuiApp->focusWindow()); + canvas.requestActivateWindow(); + + QVERIFY(canvas.rootObject() != 0); + + QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput"))); + + QVERIFY(input != 0); + QTRY_VERIFY(input->hasActiveFocus() == true); + + QSignalSpy spy(input, SIGNAL(selectionChanged())); + + simulateKey(&canvas, Qt::Key_Right, Qt::ShiftModifier); + QVERIFY(input->hasActiveFocus() == true); + QCOMPARE(input->selectedText(), QString("a")); + QCOMPARE(spy.count(), 1); + simulateKey(&canvas, Qt::Key_Right); + QVERIFY(input->hasActiveFocus() == true); + QCOMPARE(input->selectedText(), QString()); + QCOMPARE(spy.count(), 2); + simulateKey(&canvas, Qt::Key_Right); + QVERIFY(input->hasActiveFocus() == false); + QCOMPARE(input->selectedText(), QString()); + QCOMPARE(spy.count(), 2); + + simulateKey(&canvas, Qt::Key_Left); + QVERIFY(input->hasActiveFocus() == true); + QCOMPARE(spy.count(), 2); + simulateKey(&canvas, Qt::Key_Left, Qt::ShiftModifier); + QVERIFY(input->hasActiveFocus() == true); + QCOMPARE(input->selectedText(), QString("a")); + QCOMPARE(spy.count(), 3); + simulateKey(&canvas, Qt::Key_Left); + QVERIFY(input->hasActiveFocus() == true); + QCOMPARE(input->selectedText(), QString()); + QCOMPARE(spy.count(), 4); + simulateKey(&canvas, Qt::Key_Left); + QVERIFY(input->hasActiveFocus() == false); + QCOMPARE(input->selectedText(), QString()); + QCOMPARE(spy.count(), 4); +} + +void tst_qquicktextedit::moveCursorSelection_data() +{ + QTest::addColumn<QString>("testStr"); + QTest::addColumn<int>("cursorPosition"); + QTest::addColumn<int>("movePosition"); + QTest::addColumn<QQuickTextEdit::SelectionMode>("mode"); + QTest::addColumn<int>("selectionStart"); + QTest::addColumn<int>("selectionEnd"); + QTest::addColumn<bool>("reversible"); + + QTest::newRow("(t)he|characters") + << standard[0] << 0 << 1 << QQuickTextEdit::SelectCharacters << 0 << 1 << true; + QTest::newRow("do(g)|characters") + << standard[0] << 43 << 44 << QQuickTextEdit::SelectCharacters << 43 << 44 << true; + QTest::newRow("jum(p)ed|characters") + << standard[0] << 23 << 24 << QQuickTextEdit::SelectCharacters << 23 << 24 << true; + QTest::newRow("jumped( )over|characters") + << standard[0] << 26 << 27 << QQuickTextEdit::SelectCharacters << 26 << 27 << true; + QTest::newRow("(the )|characters") + << standard[0] << 0 << 4 << QQuickTextEdit::SelectCharacters << 0 << 4 << true; + QTest::newRow("( dog)|characters") + << standard[0] << 40 << 44 << QQuickTextEdit::SelectCharacters << 40 << 44 << true; + QTest::newRow("( jumped )|characters") + << standard[0] << 19 << 27 << QQuickTextEdit::SelectCharacters << 19 << 27 << true; + QTest::newRow("th(e qu)ick|characters") + << standard[0] << 2 << 6 << QQuickTextEdit::SelectCharacters << 2 << 6 << true; + QTest::newRow("la(zy d)og|characters") + << standard[0] << 38 << 42 << QQuickTextEdit::SelectCharacters << 38 << 42 << true; + QTest::newRow("jum(ped ov)er|characters") + << standard[0] << 23 << 29 << QQuickTextEdit::SelectCharacters << 23 << 29 << true; + QTest::newRow("()the|characters") + << standard[0] << 0 << 0 << QQuickTextEdit::SelectCharacters << 0 << 0 << true; + QTest::newRow("dog()|characters") + << standard[0] << 44 << 44 << QQuickTextEdit::SelectCharacters << 44 << 44 << true; + QTest::newRow("jum()ped|characters") + << standard[0] << 23 << 23 << QQuickTextEdit::SelectCharacters << 23 << 23 << true; + + QTest::newRow("<(t)he>|words") + << standard[0] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 3 << true; + QTest::newRow("<do(g)>|words") + << standard[0] << 43 << 44 << QQuickTextEdit::SelectWords << 41 << 44 << true; + QTest::newRow("<jum(p)ed>|words") + << standard[0] << 23 << 24 << QQuickTextEdit::SelectWords << 20 << 26 << true; + QTest::newRow("<jumped( )>over|words") + << standard[0] << 26 << 27 << QQuickTextEdit::SelectWords << 20 << 27 << false; + QTest::newRow("jumped<( )over>|words,reversed") + << standard[0] << 27 << 26 << QQuickTextEdit::SelectWords << 26 << 31 << false; + QTest::newRow("<(the )>quick|words") + << standard[0] << 0 << 4 << QQuickTextEdit::SelectWords << 0 << 4 << false; + QTest::newRow("<(the )quick>|words,reversed") + << standard[0] << 4 << 0 << QQuickTextEdit::SelectWords << 0 << 9 << false; + QTest::newRow("<lazy( dog)>|words") + << standard[0] << 40 << 44 << QQuickTextEdit::SelectWords << 36 << 44 << false; + QTest::newRow("lazy<( dog)>|words,reversed") + << standard[0] << 44 << 40 << QQuickTextEdit::SelectWords << 40 << 44 << false; + QTest::newRow("<fox( jumped )>over|words") + << standard[0] << 19 << 27 << QQuickTextEdit::SelectWords << 16 << 27 << false; + QTest::newRow("fox<( jumped )over>|words,reversed") + << standard[0] << 27 << 19 << QQuickTextEdit::SelectWords << 19 << 31 << false; + QTest::newRow("<th(e qu)ick>|words") + << standard[0] << 2 << 6 << QQuickTextEdit::SelectWords << 0 << 9 << true; + QTest::newRow("<la(zy d)og|words>") + << standard[0] << 38 << 42 << QQuickTextEdit::SelectWords << 36 << 44 << true; + QTest::newRow("<jum(ped ov)er>|words") + << standard[0] << 23 << 29 << QQuickTextEdit::SelectWords << 20 << 31 << true; + QTest::newRow("<()>the|words") + << standard[0] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true; + QTest::newRow("dog<()>|words") + << standard[0] << 44 << 44 << QQuickTextEdit::SelectWords << 44 << 44 << true; + QTest::newRow("jum<()>ped|words") + << standard[0] << 23 << 23 << QQuickTextEdit::SelectWords << 23 << 23 << true; + + QTest::newRow("Hello<(,)> |words") + << standard[2] << 5 << 6 << QQuickTextEdit::SelectWords << 5 << 6 << true; + QTest::newRow("Hello<(, )>world|words") + << standard[2] << 5 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false; + QTest::newRow("Hello<(, )world>|words,reversed") + << standard[2] << 7 << 5 << QQuickTextEdit::SelectWords << 5 << 12 << false; + QTest::newRow("<Hel(lo, )>world|words") + << standard[2] << 3 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false; + QTest::newRow("<Hel(lo, )world>|words,reversed") + << standard[2] << 7 << 3 << QQuickTextEdit::SelectWords << 0 << 12 << false; + QTest::newRow("<Hel(lo)>,|words") + << standard[2] << 3 << 5 << QQuickTextEdit::SelectWords << 0 << 5 << true; + QTest::newRow("Hello<()>,|words") + << standard[2] << 5 << 5 << QQuickTextEdit::SelectWords << 5 << 5 << true; + QTest::newRow("Hello,<()>|words") + << standard[2] << 6 << 6 << QQuickTextEdit::SelectWords << 6 << 6 << true; + QTest::newRow("Hello<,( )>world|words") + << standard[2] << 6 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false; + QTest::newRow("Hello,<( )world>|words,reversed") + << standard[2] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false; + QTest::newRow("Hello<,( world)>|words") + << standard[2] << 6 << 12 << QQuickTextEdit::SelectWords << 5 << 12 << false; + QTest::newRow("Hello,<( world)>|words,reversed") + << standard[2] << 12 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false; + QTest::newRow("Hello<,( world!)>|words") + << standard[2] << 6 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << false; + QTest::newRow("Hello,<( world!)>|words,reversed") + << standard[2] << 13 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false; + QTest::newRow("Hello<(, world!)>|words") + << standard[2] << 5 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << true; + QTest::newRow("world<(!)>|words") + << standard[2] << 12 << 13 << QQuickTextEdit::SelectWords << 12 << 13 << true; + QTest::newRow("world!<()>)|words") + << standard[2] << 13 << 13 << QQuickTextEdit::SelectWords << 13 << 13 << true; + QTest::newRow("world<()>!)|words") + << standard[2] << 12 << 12 << QQuickTextEdit::SelectWords << 12 << 12 << true; + + QTest::newRow("<(,)>olleH |words") + << standard[3] << 7 << 8 << QQuickTextEdit::SelectWords << 7 << 8 << true; + QTest::newRow("<dlrow( ,)>olleH|words") + << standard[3] << 6 << 8 << QQuickTextEdit::SelectWords << 1 << 8 << false; + QTest::newRow("dlrow<( ,)>olleH|words,reversed") + << standard[3] << 8 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false; + QTest::newRow("<dlrow( ,ol)leH>|words") + << standard[3] << 6 << 10 << QQuickTextEdit::SelectWords << 1 << 13 << false; + QTest::newRow("dlrow<( ,ol)leH>|words,reversed") + << standard[3] << 10 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false; + QTest::newRow(",<(ol)leH>,|words") + << standard[3] << 8 << 10 << QQuickTextEdit::SelectWords << 8 << 13 << true; + QTest::newRow(",<()>olleH|words") + << standard[3] << 8 << 8 << QQuickTextEdit::SelectWords << 8 << 8 << true; + QTest::newRow("<()>,olleH|words") + << standard[3] << 7 << 7 << QQuickTextEdit::SelectWords << 7 << 7 << true; + QTest::newRow("<dlrow( )>,olleH|words") + << standard[3] << 6 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false; + QTest::newRow("dlrow<( ),>olleH|words,reversed") + << standard[3] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false; + QTest::newRow("<(dlrow )>,olleH|words") + << standard[3] << 1 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false; + QTest::newRow("<(dlrow ),>olleH|words,reversed") + << standard[3] << 7 << 1 << QQuickTextEdit::SelectWords << 1 << 8 << false; + QTest::newRow("<(!dlrow )>,olleH|words") + << standard[3] << 0 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false; + QTest::newRow("<(!dlrow ),>olleH|words,reversed") + << standard[3] << 7 << 0 << QQuickTextEdit::SelectWords << 0 << 8 << false; + QTest::newRow("(!dlrow ,)olleH|words") + << standard[3] << 0 << 8 << QQuickTextEdit::SelectWords << 0 << 8 << true; + QTest::newRow("<(!)>dlrow|words") + << standard[3] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 1 << true; + QTest::newRow("<()>!dlrow|words") + << standard[3] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true; + QTest::newRow("!<()>dlrow|words") + << standard[3] << 1 << 1 << QQuickTextEdit::SelectWords << 1 << 1 << true; +} + +void tst_qquicktextedit::moveCursorSelection() +{ + QFETCH(QString, testStr); + QFETCH(int, cursorPosition); + QFETCH(int, movePosition); + QFETCH(QQuickTextEdit::SelectionMode, mode); + QFETCH(int, selectionStart); + QFETCH(int, selectionEnd); + QFETCH(bool, reversible); + + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }"; + QDeclarativeComponent textinputComponent(&engine); + textinputComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(textinputComponent.create()); + QVERIFY(texteditObject != 0); + + texteditObject->setCursorPosition(cursorPosition); + texteditObject->moveCursorSelection(movePosition, mode); + + QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart)); + QCOMPARE(texteditObject->selectionStart(), selectionStart); + QCOMPARE(texteditObject->selectionEnd(), selectionEnd); + + if (reversible) { + texteditObject->setCursorPosition(movePosition); + texteditObject->moveCursorSelection(cursorPosition, mode); + + QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart)); + QCOMPARE(texteditObject->selectionStart(), selectionStart); + QCOMPARE(texteditObject->selectionEnd(), selectionEnd); + } +} + +void tst_qquicktextedit::moveCursorSelectionSequence_data() +{ + 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"); + + 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; +} + +void tst_qquicktextedit::moveCursorSelectionSequence() +{ + 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\nTextEdit { text: \""+ testStr +"\"; }"; + QDeclarativeComponent texteditComponent(&engine); + texteditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create()); + QVERIFY(texteditObject != 0); + + texteditObject->setCursorPosition(cursorPosition); + + texteditObject->moveCursorSelection(movePosition1, QQuickTextEdit::SelectWords); + QCOMPARE(texteditObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start)); + QCOMPARE(texteditObject->selectionStart(), selection1Start); + QCOMPARE(texteditObject->selectionEnd(), selection1End); + + texteditObject->moveCursorSelection(movePosition2, QQuickTextEdit::SelectWords); + QCOMPARE(texteditObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start)); + QCOMPARE(texteditObject->selectionStart(), selection2Start); + QCOMPARE(texteditObject->selectionEnd(), selection2End); +} + + +void tst_qquicktextedit::mouseSelection_data() +{ + QTest::addColumn<QString>("qmlfile"); + QTest::addColumn<int>("from"); + QTest::addColumn<int>("to"); + QTest::addColumn<QString>("selectedText"); + + // import installed + QTest::newRow("on") << TESTDATA("mouseselection_true.qml") << 4 << 9 << "45678"; + QTest::newRow("off") << TESTDATA("mouseselection_false.qml") << 4 << 9 << QString(); + QTest::newRow("default") << TESTDATA("mouseselection_default.qml") << 4 << 9 << QString(); + QTest::newRow("off word selection") << TESTDATA("mouseselection_false_words.qml") << 4 << 9 << QString(); + QTest::newRow("on word selection (4,9)") << TESTDATA("mouseselection_true_words.qml") << 4 << 9 << "0123456789"; + QTest::newRow("on word selection (2,13)") << TESTDATA("mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (2,30)") << TESTDATA("mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (9,13)") << TESTDATA("mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (9,30)") << TESTDATA("mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (13,2)") << TESTDATA("mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (20,2)") << TESTDATA("mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (12,9)") << TESTDATA("mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (30,9)") << TESTDATA("mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +} + +void tst_qquicktextedit::mouseSelection() +{ + QFETCH(QString, qmlfile); + QFETCH(int, from); + QFETCH(int, to); + QFETCH(QString, selectedText); + + QQuickView canvas(QUrl::fromLocalFile(qmlfile)); + + canvas.show(); + canvas.requestActivateWindow(); + QTest::qWaitForWindowShown(&canvas); + QTRY_COMPARE(&canvas, qGuiApp->focusWindow()); + + QVERIFY(canvas.rootObject() != 0); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject()); + QVERIFY(textEditObject != 0); + + // press-and-drag-and-release from x1 to x2 + QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint(); + QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint(); + QTest::mousePress(&canvas, Qt::LeftButton, 0, p1); + QTest::mouseMove(&canvas, p2); + QTest::mouseRelease(&canvas, Qt::LeftButton, 0, p2); + QTest::qWait(50); + QTRY_COMPARE(textEditObject->selectedText(), selectedText); + + // Clicking and shift to clicking between the same points should select the same text. + textEditObject->setCursorPosition(0); + QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1); + QTest::mouseClick(&canvas, Qt::LeftButton, Qt::ShiftModifier, p2); + QTest::qWait(50); + if (!selectedText.isEmpty()) + QEXPECT_FAIL("", "QTBUG-21743", Continue); + QTRY_COMPARE(textEditObject->selectedText(), selectedText); +} + +void tst_qquicktextedit::dragMouseSelection() +{ + QString qmlfile = TESTDATA("mouseselection_true.qml"); + + QQuickView canvas(QUrl::fromLocalFile(qmlfile)); + + canvas.show(); + canvas.requestActivateWindow(); + QTest::qWaitForWindowShown(&canvas); + QTRY_COMPARE(&canvas, qGuiApp->focusWindow()); + + QVERIFY(canvas.rootObject() != 0); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject()); + QVERIFY(textEditObject != 0); + + // press-and-drag-and-release from x1 to x2 + int x1 = 10; + int x2 = 70; + int y = textEditObject->height()/2; + QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y)); + QTest::mouseMove(&canvas, QPoint(x2, y)); + QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y)); + QTest::qWait(300); + QString str1; + QTRY_VERIFY((str1 = textEditObject->selectedText()).length() > 3); + + // press and drag the current selection. + x1 = 40; + x2 = 100; + QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y)); + QTest::mouseMove(&canvas, QPoint(x2, y)); + QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y)); + QTest::qWait(300); + QString str2; + QTRY_VERIFY((str2 = textEditObject->selectedText()).length() > 3); + + QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and not the first moved. +} + +void tst_qquicktextedit::mouseSelectionMode_data() +{ + QTest::addColumn<QString>("qmlfile"); + QTest::addColumn<bool>("selectWords"); + + // import installed + QTest::newRow("SelectWords") << TESTDATA("mouseselectionmode_words.qml") << true; + QTest::newRow("SelectCharacters") << TESTDATA("mouseselectionmode_characters.qml") << false; + QTest::newRow("default") << TESTDATA("mouseselectionmode_default.qml") << false; +} + +void tst_qquicktextedit::mouseSelectionMode() +{ + QFETCH(QString, qmlfile); + QFETCH(bool, selectWords); + + QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + QQuickView canvas(QUrl::fromLocalFile(qmlfile)); + + canvas.show(); + canvas.requestActivateWindow(); + QTest::qWaitForWindowShown(&canvas); + QTRY_COMPARE(&canvas, qGuiApp->focusWindow()); + + QVERIFY(canvas.rootObject() != 0); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject()); + QVERIFY(textEditObject != 0); + + // press-and-drag-and-release from x1 to x2 + int x1 = 10; + int x2 = 70; + int y = textEditObject->height()/2; + QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y)); + QTest::mouseMove(&canvas, QPoint(x2, y)); + //QTest::mouseMove(canvas, QPoint(x2,y)); // doesn't work +// QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); +// QGuiApplication::sendEvent(&canvas, &mv); + QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y)); + QString str = textEditObject->selectedText(); + if (selectWords) { + QTRY_COMPARE(textEditObject->selectedText(), text); + } else { + QTRY_VERIFY(textEditObject->selectedText().length() > 3); + QVERIFY(str != text); + } +} + +void tst_qquicktextedit::inputMethodHints() +{ + QQuickView canvas(QUrl::fromLocalFile(TESTDATA("inputmethodhints.qml"))); + canvas.show(); + canvas.requestActivateWindow(); + + QVERIFY(canvas.rootObject() != 0); + QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject()); + QVERIFY(textEditObject != 0); + QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText); + textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly); + QVERIFY(textEditObject->inputMethodHints() & Qt::ImhUppercaseOnly); +} + +void tst_qquicktextedit::positionAt() +{ + QQuickView canvas(QUrl::fromLocalFile(TESTDATA("positionAt.qml"))); + QVERIFY(canvas.rootObject() != 0); + canvas.show(); + canvas.requestActivateWindow(); + canvas.requestActivateWindow(); + QTest::qWaitForWindowShown(&canvas); + + QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject()); + QVERIFY(texteditObject != 0); + + QFontMetrics fm(texteditObject->font()); + const int y0 = fm.height() / 2; + const int y1 = fm.height() * 3 / 2; + + int pos = texteditObject->positionAt(texteditObject->width()/2, y0); + int width = 0; + if (!qmlDisableDistanceField()) { + QTextLayout layout(texteditObject->text().left(pos)); + + { + QTextOption option; + option.setUseDesignMetrics(true); + layout.setTextOption(option); + } + + layout.beginLayout(); + QTextLine line = layout.createLine(); + layout.endLayout(); + + width = ceil(line.horizontalAdvance()); + + } else { + width = fm.width(texteditObject->text().left(pos)); + } + + + int diff = abs(int(width-texteditObject->width()/2)); + + QEXPECT_FAIL("", "QTBUG-21689", Abort); + // some tollerance for different fonts. +#ifdef Q_OS_LINUX + QVERIFY(diff < 2); +#else + QVERIFY(diff < 5); +#endif + + const qreal x0 = texteditObject->positionToRectangle(pos).x(); + const qreal x1 = texteditObject->positionToRectangle(pos + 1).x(); + + QString preeditText = texteditObject->text().mid(0, pos); + texteditObject->setText(texteditObject->text().mid(pos)); + texteditObject->setCursorPosition(0); + + QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>()); + QGuiApplication::sendEvent(&canvas, &inputEvent); + + // Check all points within the preedit text return the same position. + QCOMPARE(texteditObject->positionAt(0, y0), 0); + QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0); + QCOMPARE(texteditObject->positionAt(x0, y0), 0); + + // Verify positioning returns to normal after the preedit text. + QCOMPARE(texteditObject->positionAt(x1, y0), 1); + QCOMPARE(texteditObject->positionToRectangle(1).x(), x1); + + QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0); +} + +void tst_qquicktextedit::cursorDelegate() +{ + QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorTest.qml"))); + view.show(); + view.requestActivateWindow(); + QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject"); + QVERIFY(textEditObject != 0); + QVERIFY(textEditObject->findChild<QQuickItem*>("cursorInstance")); + //Test Delegate gets created + textEditObject->setFocus(true); + QQuickItem* delegateObject = textEditObject->findChild<QQuickItem*>("cursorInstance"); + QVERIFY(delegateObject); + QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello")); + //Test Delegate gets moved + for (int i=0; i<= textEditObject->text().length(); i++) { + textEditObject->setCursorPosition(i); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + } + // Clear preedit text; + QInputMethodEvent event; + QGuiApplication::sendEvent(&view, &event); + + + // Test delegate gets moved on mouse press. + textEditObject->setSelectByMouse(true); + textEditObject->setCursorPosition(0); + const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint(); + QTest::mouseClick(&view, Qt::LeftButton, 0, point1); + QTest::qWait(50); + QTRY_VERIFY(textEditObject->cursorPosition() != 0); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + + // Test delegate gets moved on mouse drag + textEditObject->setCursorPosition(0); + const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint(); + QTest::mousePress(&view, Qt::LeftButton, 0, point1); + QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QGuiApplication::sendEvent(&view, &mv); + QTest::mouseRelease(&view, Qt::LeftButton, 0, point2); + QTest::qWait(50); + QTRY_COMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + + textEditObject->setReadOnly(true); + textEditObject->setCursorPosition(0); + QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint()); + QTest::qWait(50); + QTRY_VERIFY(textEditObject->cursorPosition() != 0); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + + textEditObject->setCursorPosition(0); + QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint()); + QTest::qWait(50); + QTRY_VERIFY(textEditObject->cursorPosition() != 0); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + + textEditObject->setCursorPosition(0); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + //Test Delegate gets deleted + textEditObject->setCursorDelegate(0); + QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance")); +} + +void tst_qquicktextedit::cursorVisible() +{ + QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorVisible.qml"))); + view.show(); + view.requestActivateWindow(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(&view, qGuiApp->focusWindow()); + + QQuickTextEdit edit; + QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool))); + + QCOMPARE(edit.isCursorVisible(), false); + + edit.setCursorVisible(true); + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 1); + + edit.setCursorVisible(false); + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 2); + + edit.setFocus(true); + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 2); + + edit.setParentItem(view.rootObject()); + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 3); + + edit.setFocus(false); + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 4); + + edit.setFocus(true); + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 5); + + QEXPECT_FAIL("", "Most likely a side-effect of QTBUG-21489", Abort); + view.setWindowState(Qt::WindowNoState); + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 6); + + view.requestActivateWindow(); + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 7); + + // on mac, setActiveWindow(0) on mac does not deactivate the current application + // (you have to switch to a different app or hide the current app to trigger this) +#if !defined(Q_WS_MAC) + // on mac, setActiveWindow(0) on mac does not deactivate the current application + // (you have to switch to a different app or hide the current app to trigger this) +// QApplication::setActiveWindow(0); +// QTRY_COMPARE(QApplication::focusWindow(), static_cast<QWidget *>(0)); +// QCOMPARE(edit.isCursorVisible(), false); +// QCOMPARE(spy.count(), 8); + +// view.requestActivateWindow(); +// QTest::qWaitForWindowShown(&view); +// QTRY_COMPARE(view.windowState(), Qt::WindowActive); +// QCOMPARE(edit.isCursorVisible(), true); +// QCOMPARE(spy.count(), 9); +#endif +} + +void tst_qquicktextedit::delegateLoading_data() +{ + QTest::addColumn<QString>("qmlfile"); + QTest::addColumn<QString>("error"); + + // import installed + QTest::newRow("pass") << "cursorHttpTestPass.qml" << ""; + QTest::newRow("fail1") << "cursorHttpTestFail1.qml" << "http://localhost:42332/FailItem.qml: Remote host closed the connection "; + QTest::newRow("fail2") << "cursorHttpTestFail2.qml" << "http://localhost:42332/ErrItem.qml:4:5: Fungus is not a type "; +} + +void tst_qquicktextedit::delegateLoading() +{ + QFETCH(QString, qmlfile); + QFETCH(QString, error); + + TestHTTPServer server(42332); + server.serveDirectory(TESTDATA("httpfail"), TestHTTPServer::Disconnect); + server.serveDirectory(TESTDATA("httpslow"), TestHTTPServer::Delay); + server.serveDirectory(TESTDATA("http")); + + QQuickView view(QUrl(QLatin1String("http://localhost:42332/") + qmlfile)); + view.show(); + view.requestActivateWindow(); + + if (!error.isEmpty()) { + QTest::ignoreMessage(QtWarningMsg, error.toUtf8()); + QTRY_VERIFY(view.status()==QQuickView::Error); + QTRY_VERIFY(!view.rootObject()); // there is fail item inside this test + } else { + QTRY_VERIFY(view.rootObject());//Wait for loading to finish. + QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject"); + // view.rootObject()->dumpObjectTree(); + QVERIFY(textEditObject != 0); + textEditObject->setFocus(true); + QQuickItem *delegate; + delegate = view.rootObject()->findChild<QQuickItem*>("delegateOkay"); + QVERIFY(delegate); + delegate = view.rootObject()->findChild<QQuickItem*>("delegateSlow"); + QVERIFY(delegate); + + delete delegate; + } + + + //A test should be added here with a component which is ready but component.create() returns null + //Not sure how to accomplish this with QQuickTextEdits cursor delegate + //###This was only needed for code coverage, and could be a case of overzealous defensive programming + //delegate = view.rootObject()->findChild<QQuickItem*>("delegateErrorB"); + //QVERIFY(!delegate); +} + +/* +TextEdit element should only handle left/right keys until the cursor reaches +the extent of the text, then they should ignore the keys. +*/ +void tst_qquicktextedit::navigation() +{ + QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml"))); + canvas.show(); + canvas.requestActivateWindow(); + + QVERIFY(canvas.rootObject() != 0); + + QQuickItem *input = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput"))); + + QVERIFY(input != 0); + QTRY_VERIFY(input->hasActiveFocus() == true); + simulateKey(&canvas, Qt::Key_Left); + QVERIFY(input->hasActiveFocus() == false); + simulateKey(&canvas, Qt::Key_Right); + QVERIFY(input->hasActiveFocus() == true); + simulateKey(&canvas, Qt::Key_Right); + QVERIFY(input->hasActiveFocus() == true); + simulateKey(&canvas, Qt::Key_Right); + QVERIFY(input->hasActiveFocus() == false); + simulateKey(&canvas, Qt::Key_Left); + QVERIFY(input->hasActiveFocus() == true); +} + +void tst_qquicktextedit::copyAndPaste() { +#ifndef QT_NO_CLIPBOARD + +#ifdef Q_WS_MAC + { + PasteboardRef pasteboard; + OSStatus status = PasteboardCreate(0, &pasteboard); + if (status == noErr) + CFRelease(pasteboard); + else + QSKIP("This machine doesn't support the clipboard"); + } +#endif + + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }"; + QDeclarativeComponent textEditComponent(&engine); + textEditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create()); + QVERIFY(textEdit != 0); + + // copy and paste + QCOMPARE(textEdit->text().length(), 12); + textEdit->select(0, textEdit->text().length());; + textEdit->copy(); + QCOMPARE(textEdit->selectedText(), QString("Hello world!")); + QCOMPARE(textEdit->selectedText().length(), 12); + textEdit->setCursorPosition(0); + QVERIFY(textEdit->canPaste()); + textEdit->paste(); + QCOMPARE(textEdit->text(), QString("Hello world!Hello world!")); + QCOMPARE(textEdit->text().length(), 24); + + // canPaste + QVERIFY(textEdit->canPaste()); + textEdit->setReadOnly(true); + QVERIFY(!textEdit->canPaste()); + textEdit->setReadOnly(false); + QVERIFY(textEdit->canPaste()); + + // QTBUG-12339 + // test that document and internal text attribute are in sync + QQuickItemPrivate* pri = QQuickItemPrivate::get(textEdit); + QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(pri); + QCOMPARE(textEdit->text(), editPrivate->text); + + // select word + textEdit->setCursorPosition(0); + textEdit->selectWord(); + QCOMPARE(textEdit->selectedText(), QString("Hello")); + + // select all and cut + textEdit->selectAll(); + textEdit->cut(); + QCOMPARE(textEdit->text().length(), 0); + textEdit->paste(); + QCOMPARE(textEdit->text(), QString("Hello world!Hello world!")); + QCOMPARE(textEdit->text().length(), 24); +#endif +} + +void tst_qquicktextedit::canPaste() { +#ifndef QT_NO_CLIPBOARD + + QGuiApplication::clipboard()->setText("Some text"); + + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }"; + QDeclarativeComponent textEditComponent(&engine); + textEditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create()); + QVERIFY(textEdit != 0); + + // check initial value - QTBUG-17765 + QTextControl tc; + QCOMPARE(textEdit->canPaste(), tc.canPaste()); + +#endif +} + +void tst_qquicktextedit::canPasteEmpty() { +#ifndef QT_NO_CLIPBOARD + + QGuiApplication::clipboard()->clear(); + + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }"; + QDeclarativeComponent textEditComponent(&engine); + textEditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create()); + QVERIFY(textEdit != 0); + + // check initial value - QTBUG-17765 + QTextControl tc; + QCOMPARE(textEdit->canPaste(), tc.canPaste()); + +#endif +} + +void tst_qquicktextedit::readOnly() +{ + QQuickView canvas(QUrl::fromLocalFile(TESTDATA("readOnly.qml"))); + canvas.show(); + canvas.requestActivateWindow(); + + QVERIFY(canvas.rootObject() != 0); + + QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput"))); + + QVERIFY(edit != 0); + QTRY_VERIFY(edit->hasActiveFocus() == true); + QVERIFY(edit->isReadOnly() == true); + QString initial = edit->text(); + for (int k=Qt::Key_0; k<=Qt::Key_Z; k++) + simulateKey(&canvas, k); + simulateKey(&canvas, Qt::Key_Return); + simulateKey(&canvas, Qt::Key_Space); + simulateKey(&canvas, Qt::Key_Escape); + QCOMPARE(edit->text(), initial); + + edit->setCursorPosition(3); + edit->setReadOnly(false); + QCOMPARE(edit->isReadOnly(), false); + QCOMPARE(edit->cursorPosition(), edit->text().length()); +} + +void tst_qquicktextedit::simulateKey(QQuickView *view, int key, Qt::KeyboardModifiers modifiers) +{ + QKeyEvent press(QKeyEvent::KeyPress, key, modifiers); + QKeyEvent release(QKeyEvent::KeyRelease, key, modifiers); + + QGuiApplication::sendEvent(view, &press); + QGuiApplication::sendEvent(view, &release); +} + + +#ifndef QTBUG_21691 +class MyInputContext : public QInputContext +{ +public: + MyInputContext() : updateReceived(false), eventType(QEvent::None) {} + ~MyInputContext() {} + + QString identifierName() { return QString(); } + QString language() { return QString(); } + + void reset() {} + + bool isComposing() const { return false; } + + void update() { updateReceived = true; } + + void sendPreeditText(const QString &text, int cursor) + { + QList<QInputMethodEvent::Attribute> attributes; + attributes.append(QInputMethodEvent::Attribute( + QInputMethodEvent::Cursor, cursor, text.length(), QVariant())); + + QInputMethodEvent event(text, attributes); + sendEvent(event); + } + + void mouseHandler(int x, QMouseEvent *event) + { + cursor = x; + eventType = event->type(); + eventPosition = event->pos(); + eventGlobalPosition = event->globalPos(); + eventButton = event->button(); + eventButtons = event->buttons(); + eventModifiers = event->modifiers(); + } + + bool updateReceived; + int cursor; + QEvent::Type eventType; + QPoint eventPosition; + QPoint eventGlobalPosition; + Qt::MouseButton eventButton; + Qt::MouseButtons eventButtons; + Qt::KeyboardModifiers eventModifiers; +}; +#endif + +void tst_qquicktextedit::textInput() +{ + QQuickView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml"))); + view.show(); + view.requestActivateWindow(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(&view, qGuiApp->focusWindow()); + QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject()); + QVERIFY(edit); + QVERIFY(edit->hasActiveFocus() == true); + + // test that input method event is committed + QInputMethodEvent event; + event.setCommitString( "Hello world!", 0, 0); + QGuiApplication::sendEvent(&view, &event); + QEXPECT_FAIL("", "QTBUG-21689", Abort); + QCOMPARE(edit->text(), QString("Hello world!")); + + // QTBUG-12339 + // test that document and internal text attribute are in sync + QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(edit)); + QCOMPARE(editPrivate->text, QString("Hello world!")); +} + +void tst_qquicktextedit::openInputPanel() +{ + QQuickView view(QUrl::fromLocalFile(TESTDATA("openInputPanel.qml"))); + view.show(); + view.requestActivateWindow(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(&view, qGuiApp->focusWindow()); + + QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject()); + QVERIFY(edit); + + // check default values + QVERIFY(edit->focusOnPress()); + QVERIFY(!edit->hasActiveFocus()); + qDebug() << &edit << qApp->inputPanel()->inputItem(); + QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0)); + QEXPECT_FAIL("", "QTBUG-21946", Abort); + QCOMPARE(qApp->inputPanel()->visible(), false); + + // input panel should open on focus + QPoint centerPoint(view.width()/2, view.height()/2); + Qt::KeyboardModifiers noModifiers = 0; + QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint); + QGuiApplication::processEvents(); + QVERIFY(edit->hasActiveFocus()); + QCOMPARE(qApp->inputPanel()->inputItem(), edit); + QCOMPARE(qApp->inputPanel()->visible(), true); + QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint); + + // input panel should be re-opened when pressing already focused TextEdit + qApp->inputPanel()->hide(); + QCOMPARE(qApp->inputPanel()->visible(), false); + QVERIFY(edit->hasActiveFocus()); + QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint); + QGuiApplication::processEvents(); + QCOMPARE(qApp->inputPanel()->visible(), true); + QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint); + + // input panel should stay visible if focus is lost to another text editor + QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged())); + QQuickTextEdit anotherEdit; + anotherEdit.setParentItem(view.rootObject()); + anotherEdit.setFocus(true); + QCOMPARE(qApp->inputPanel()->visible(), true); + QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherEdit)); + QCOMPARE(inputPanelVisibilitySpy.count(), 0); + + anotherEdit.setFocus(false); + QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0)); + QCOMPARE(view.activeFocusItem(), view.rootItem()); + anotherEdit.setFocus(true); + + // input item should be null if focus is lost to an item that doesn't accept inputs + QQuickItem item; + item.setParentItem(view.rootObject()); + item.setFocus(true); + QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0)); + QCOMPARE(view.activeFocusItem(), &item); + + qApp->inputPanel()->hide(); + + // input panel should not be opened if TextEdit is read only + edit->setReadOnly(true); + edit->setFocus(true); + QCOMPARE(qApp->inputPanel()->visible(), false); + QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint); + QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint); + QGuiApplication::processEvents(); + QCOMPARE(qApp->inputPanel()->visible(), false); + + // input panel should not be opened if focusOnPress is set to false + edit->setFocusOnPress(false); + edit->setFocus(false); + edit->setFocus(true); + QCOMPARE(qApp->inputPanel()->visible(), false); + QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint); + QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint); + QCOMPARE(qApp->inputPanel()->visible(), false); + + // input panel should open when openSoftwareInputPanel is called + edit->openSoftwareInputPanel(); + QCOMPARE(qApp->inputPanel()->visible(), true); + + // input panel should close when closeSoftwareInputPanel is called + edit->closeSoftwareInputPanel(); + QCOMPARE(qApp->inputPanel()->visible(), false); +} + +void tst_qquicktextedit::geometrySignals() +{ + QDeclarativeComponent component(&engine, TESTDATA("geometrySignals.qml")); + QObject *o = component.create(); + QVERIFY(o); + QCOMPARE(o->property("bindingWidth").toInt(), 400); + QCOMPARE(o->property("bindingHeight").toInt(), 500); + delete o; +} + +void tst_qquicktextedit::pastingRichText_QTBUG_14003() +{ +#ifndef QT_NO_CLIPBOARD + QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.PlainText }"; + QDeclarativeComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QQuickTextEdit *obj = qobject_cast<QQuickTextEdit*>(component.create()); + + QTRY_VERIFY(obj != 0); + QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText); + + QMimeData *mData = new QMimeData; + mData->setHtml("<font color=\"red\">Hello</font>"); + QGuiApplication::clipboard()->setMimeData(mData); + + obj->paste(); + QTRY_VERIFY(obj->text() == ""); + QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText); +#endif +} + +void tst_qquicktextedit::implicitSize_data() +{ + QTest::addColumn<QString>("text"); + QTest::addColumn<QString>("wrap"); + QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.NoWrap"; + QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.NoWrap"; + QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.Wrap"; + QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.Wrap"; +} + +void tst_qquicktextedit::implicitSize() +{ + QFETCH(QString, text); + QFETCH(QString, wrap); + QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }"; + QDeclarativeComponent textComponent(&engine); + textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create()); + + QVERIFY(textObject->width() < textObject->implicitWidth()); + QVERIFY(textObject->height() == textObject->implicitHeight()); + + textObject->resetWidth(); + QVERIFY(textObject->width() == textObject->implicitWidth()); + QVERIFY(textObject->height() == textObject->implicitHeight()); +} + +void tst_qquicktextedit::testQtQuick11Attributes() +{ + QFETCH(QString, code); + QFETCH(QString, warning); + QFETCH(QString, error); + + QDeclarativeEngine engine; + QObject *obj; + + QDeclarativeComponent valid(&engine); + valid.setData("import QtQuick 2.0; TextEdit { " + code.toUtf8() + " }", QUrl("")); + obj = valid.create(); + QVERIFY(obj); + QVERIFY(valid.errorString().isEmpty()); + delete obj; + + QDeclarativeComponent invalid(&engine); + invalid.setData("import QtQuick 1.0; TextEdit { " + code.toUtf8() + " }", QUrl("")); + QTest::ignoreMessage(QtWarningMsg, warning.toUtf8()); + obj = invalid.create(); + QCOMPARE(invalid.errorString(), error); + delete obj; +} + +void tst_qquicktextedit::testQtQuick11Attributes_data() +{ + QTest::addColumn<QString>("code"); + QTest::addColumn<QString>("warning"); + QTest::addColumn<QString>("error"); + + QTest::newRow("canPaste") << "property bool foo: canPaste" + << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste" + << ""; + + QTest::newRow("lineCount") << "property int foo: lineCount" + << "<Unknown File>:1: ReferenceError: Can't find variable: lineCount" + << ""; + + QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)" + << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection" + << ""; + + QTest::newRow("deselect") << "Component.onCompleted: deselect()" + << "<Unknown File>:1: ReferenceError: Can't find variable: deselect" + << ""; + + QTest::newRow("onLinkActivated") << "onLinkActivated: {}" + << "QDeclarativeComponent: Component is not ready" + << ":1 \"TextEdit.onLinkActivated\" is not available in QtQuick 1.0.\n"; +} + +void tst_qquicktextedit::preeditMicroFocus() +{ +#ifdef QTBUG_21691 + QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort); + QVERIFY(false); +#else + QString preeditText = "super"; + + QQuickView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml"))); + view.show(); + view.requestActivateWindow(); + QTest::qWaitForWindowShown(&view); + + QTRY_COMPARE(&view, qGuiApp->focusWindow()); + QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject()); + QVERIFY(edit); + + QSignalSpy cursorRectangleSpy(edit, SIGNAL(cursorRectangleChanged())); + + QRect currentRect; + QRect previousRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect(); + + // Verify that the micro focus rect is positioned the same for position 0 as + // it would be if there was no preedit text. + ic.updateReceived = false; + ic.sendPreeditText(preeditText, 0); + currentRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect(); + QCOMPARE(currentRect, previousRect); +#if defined(Q_WS_X11) || defined(Q_WS_QWS) + QCOMPARE(ic.updateReceived, false); // The cursor position hasn't changed. +#endif + QCOMPARE(cursorRectangleSpy.count(), 0); + + // Verify that the micro focus rect moves to the left as the cursor position + // is incremented. + for (int i = 1; i <= 5; ++i) { + ic.updateReceived = false; + ic.sendPreeditText(preeditText, i); + currentRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect(); + QVERIFY(previousRect.left() < currentRect.left()); +#if defined(Q_WS_X11) || defined(Q_WS_QWS) + QCOMPARE(ic.updateReceived, true); +#endif + QVERIFY(cursorRectangleSpy.count() > 0); + cursorRectangleSpy.clear(); + previousRect = currentRect; + } + + // Verify that if there is no preedit cursor then the micro focus rect is the + // same as it would be if it were positioned at the end of the preedit text. + ic.sendPreeditText(preeditText, 0); + ic.updateReceived = false; + ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>())); + currentRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect(); + QCOMPARE(currentRect, previousRect); +#if defined(Q_WS_X11) || defined(Q_WS_QWS) + QCOMPARE(ic.updateReceived, true); +#endif + QVERIFY(cursorRectangleSpy.count() > 0); +#endif +} + +void tst_qquicktextedit::inputContextMouseHandler() +{ + +#ifdef QTBUG_21691 + QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort); + QVERIFY(false); +#else + QString text = "supercalifragisiticexpialidocious!"; + + QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml"))); + MyInputContext ic; + // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus + // and QWidget won't allow an input context to be set when the flag is not set. + view.setAttribute(Qt::WA_InputMethodEnabled, true); + view.setInputContext(&ic); + view.setAttribute(Qt::WA_InputMethodEnabled, false); + view.show(); + view.requestActivateWindow(); + QTest::qWaitForWindowShown(&view); + + QTRY_COMPARE(&view, qGuiApp->focusWindow()); + QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject()); + QVERIFY(edit); + edit->setCursorPosition(12); + + QFontMetricsF fm(edit->font()); + const qreal y = fm.height() / 2; + + QPoint position2 = edit->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint(); + QPoint position8 = edit->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint(); + QPoint position20 = edit->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint(); + QPoint position27 = edit->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint(); + QPoint globalPosition2 = view.mapToGlobal(position2); + QPoint globalposition8 = view.mapToGlobal(position8); + QPoint globalposition20 = view.mapToGlobal(position20); + QPoint globalposition27 = view.mapToGlobal(position27); + + ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>())); + + QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2); + QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick); + QCOMPARE(ic.eventPosition, position2); + QCOMPARE(ic.eventGlobalPosition, globalPosition2); + QCOMPARE(ic.eventButton, Qt::LeftButton); + QCOMPARE(ic.eventModifiers, Qt::NoModifier); + QVERIFY(ic.cursor < 0); + ic.eventType = QEvent::None; + + QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2); + QCOMPARE(ic.eventType, QEvent::MouseButtonPress); + QCOMPARE(ic.eventPosition, position2); + QCOMPARE(ic.eventGlobalPosition, globalPosition2); + QCOMPARE(ic.eventButton, Qt::LeftButton); + QCOMPARE(ic.eventModifiers, Qt::NoModifier); + QVERIFY(ic.cursor < 0); + ic.eventType = QEvent::None; + + { QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QGuiApplication::sendEvent(&view, &mv); } + QCOMPARE(ic.eventType, QEvent::None); + + { QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QGuiApplication::sendEvent(&view, &mv); } + QCOMPARE(ic.eventType, QEvent::MouseMove); + QCOMPARE(ic.eventPosition, position27); + QCOMPARE(ic.eventGlobalPosition, globalposition27); + QCOMPARE(ic.eventButton, Qt::LeftButton); + QCOMPARE(ic.eventModifiers, Qt::NoModifier); + QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); // 15 is expected but some platforms may be off by one. + ic.eventType = QEvent::None; + + QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27); + QCOMPARE(ic.eventType, QEvent::MouseButtonRelease); + QCOMPARE(ic.eventPosition, position27); + QCOMPARE(ic.eventGlobalPosition, globalposition27); + QCOMPARE(ic.eventButton, Qt::LeftButton); + QCOMPARE(ic.eventModifiers, Qt::NoModifier); + QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); + ic.eventType = QEvent::None; + + // And in the other direction. + QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27); + QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick); + QCOMPARE(ic.eventPosition, position27); + QCOMPARE(ic.eventGlobalPosition, globalposition27); + QCOMPARE(ic.eventButton, Qt::LeftButton); + QCOMPARE(ic.eventModifiers, Qt::ControlModifier); + QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); + ic.eventType = QEvent::None; + + QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27); + QCOMPARE(ic.eventType, QEvent::MouseButtonPress); + QCOMPARE(ic.eventPosition, position27); + QCOMPARE(ic.eventGlobalPosition, globalposition27); + QCOMPARE(ic.eventButton, Qt::RightButton); + QCOMPARE(ic.eventModifiers, Qt::ControlModifier); + QVERIFY(ic.cursor >= 14 && ic.cursor <= 16); + ic.eventType = QEvent::None; + + { QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier); + QGuiApplication::sendEvent(&view, &mv); } + QCOMPARE(ic.eventType, QEvent::MouseMove); + QCOMPARE(ic.eventPosition, position20); + QCOMPARE(ic.eventGlobalPosition, globalposition20); + QCOMPARE(ic.eventButton, Qt::RightButton); + QCOMPARE(ic.eventModifiers, Qt::ControlModifier); + QVERIFY(ic.cursor >= 7 && ic.cursor <= 9); + ic.eventType = QEvent::None; + + { QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier); + QGuiApplication::sendEvent(&view, &mv); } + QCOMPARE(ic.eventType, QEvent::None); + + QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2); + QCOMPARE(ic.eventType, QEvent::MouseButtonRelease); + QCOMPARE(ic.eventPosition, position2); + QCOMPARE(ic.eventGlobalPosition, globalPosition2); + QCOMPARE(ic.eventButton, Qt::RightButton); + QCOMPARE(ic.eventModifiers, Qt::ControlModifier); + QVERIFY(ic.cursor < 0); + ic.eventType = QEvent::None; +#endif +} + +void tst_qquicktextedit::inputMethodComposing() +{ + QString text = "supercalifragisiticexpialidocious!"; + + QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml"))); + view.show(); + view.requestActivateWindow(); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(&view, qGuiApp->focusWindow()); + QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject()); + QVERIFY(edit); + QSignalSpy spy(edit, SIGNAL(inputMethodComposingChanged())); + edit->setCursorPosition(12); + + QCOMPARE(edit->isInputMethodComposing(), false); + + { + QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>()); + QGuiApplication::sendEvent(edit, &event); + } + + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 1); + + { + QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>()); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(spy.count(), 1); + + { + QInputMethodEvent event; + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 2); +} + +void tst_qquicktextedit::cursorRectangleSize() +{ + QQuickView *canvas = new QQuickView(QUrl::fromLocalFile(TESTDATA("CursorRect.qml"))); + QVERIFY(canvas->rootObject() != 0); + canvas->show(); + canvas->requestActivateWindow(); + QTest::qWaitForWindowShown(canvas); + + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(canvas->rootObject()); + QVERIFY(textEdit != 0); + textEdit->setFocus(Qt::OtherFocusReason); + QRectF cursorRect = textEdit->positionToRectangle(textEdit->cursorPosition()); + QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImCursorRectangle).toRectF(); + QInputMethodQueryEvent event(Qt::ImCursorRectangle); + qApp->sendEvent(qApp->inputPanel()->inputItem(), &event); + + QRectF microFocusFromApp = event.value(Qt::ImCursorRectangle).toRectF(); + + QCOMPARE(microFocusFromScene.size(), cursorRect.size()); + QCOMPARE(microFocusFromApp.size(), cursorRect.size()); + + delete canvas; +} + +void tst_qquicktextedit::emptytags_QTBUG_22058() +{ + QQuickView canvas(QUrl::fromLocalFile(TESTDATA("qtbug-22058.qml"))); + QVERIFY(canvas.rootObject() != 0); + + canvas.show(); + canvas.requestActivateWindow(); + QTest::qWaitForWindowShown(&canvas); + QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("inputField"))); + QVERIFY(input->hasActiveFocus()); + + QInputMethodEvent event("", QList<QInputMethodEvent::Attribute>()); + event.setCommitString("<b>Bold<"); + QGuiApplication::sendEvent(input, &event); + QCOMPARE(input->text(), QString("<b>Bold<")); + event.setCommitString(">"); + QEXPECT_FAIL("", "Entering empty tags into a TextEdit asserts - QTBUG-22058", Abort); + QVERIFY(false); + QGuiApplication::sendEvent(input, &event); + QCOMPARE(input->text(), QString("<b>Bold<>")); +} + +QTEST_MAIN(tst_qquicktextedit) + +#include "tst_qquicktextedit.moc" |