diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 20c35f42f2d5cb1121e164ec3721ffd604f4ff88..cd49377822392b3cda62536d68684411cb47fa75 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -158,6 +158,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickScale>(uri,major,minor,"Scale"); qmlRegisterType<QQuickText>(uri,major,minor,"Text"); qmlRegisterType<QQuickTextEdit>(uri,major,minor,"TextEdit"); + qmlRegisterType<QQuickTextEdit,1>(uri,2,1,"TextEdit"); qmlRegisterType<QQuickTextInput>(uri,major,minor,"TextInput"); qmlRegisterType<QQuickViewSection>(uri,major,minor,"ViewSection"); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index dabbc96614f1100e466af1a0c9c8578af170f548..e30b9cb3fdd429d262841c90b8b9c3dbe034fa84 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1238,6 +1238,44 @@ void QQuickTextEdit::componentComplete() if (d->cursorComponent && isCursorVisible()) QQuickTextUtil::createCursor(d); } + +/*! + \qmlproperty bool QtQuick2::TextEdit::selectByKeyboard + \since QtQuick 2.1 + + Defaults to true when the editor is editable, and false + when read-only. + + If true, the user can use the keyboard to select text + even if the editor is read-only. If false, the user + cannot use the keyboard to select text even if the + editor is editable. + + \sa readOnly +*/ +bool QQuickTextEdit::selectByKeyboard() const +{ + Q_D(const QQuickTextEdit); + if (d->selectByKeyboardSet) + return d->selectByKeyboard; + return !isReadOnly(); +} + +void QQuickTextEdit::setSelectByKeyboard(bool on) +{ + Q_D(QQuickTextEdit); + bool was = selectByKeyboard(); + d->selectByKeyboardSet = true; + if (was != on) { + d->selectByKeyboard = on; + if (on) + d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard); + else + d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard); + emit selectByKeyboardChanged(on); + } +} + /*! \qmlproperty bool QtQuick2::TextEdit::selectByMouse @@ -1316,8 +1354,12 @@ void QQuickTextEdit::setReadOnly(bool r) Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse; if (d->selectByMouse) flags = flags | Qt::TextSelectableByMouse; + if (d->selectByKeyboardSet && d->selectByKeyboard) + flags = flags | Qt::TextSelectableByKeyboard; + else if (!d->selectByKeyboardSet && !r) + flags = flags | Qt::TextSelectableByKeyboard; if (!r) - flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable; + flags = flags | Qt::TextEditable; d->control->setTextInteractionFlags(flags); if (!r) d->control->moveCursor(QTextCursor::End); @@ -1327,6 +1369,8 @@ void QQuickTextEdit::setReadOnly(bool r) #endif q_canPasteChanged(); emit readOnlyChanged(r); + if (!d->selectByKeyboardSet) + emit selectByKeyboardChanged(!r); } bool QQuickTextEdit::isReadOnly() const diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index 255c8ac6709f29b8d3275ee423470f547e2901fa..8a2d9b1e9254c96325156b7ab3e2af77a7c82f67 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -90,6 +90,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem #ifndef QT_NO_IM Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ inputMethodHints WRITE setInputMethodHints NOTIFY inputMethodHintsChanged) #endif + Q_PROPERTY(bool selectByKeyboard READ selectByKeyboard WRITE setSelectByKeyboard NOTIFY selectByKeyboardChanged REVISION 1) Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged) Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged) @@ -201,6 +202,9 @@ public: void setInputMethodHints(Qt::InputMethodHints hints); #endif + bool selectByKeyboard() const; + void setSelectByKeyboard(bool); + bool selectByMouse() const; void setSelectByMouse(bool); @@ -274,6 +278,7 @@ Q_SIGNALS: void activeFocusOnPressChanged(bool activeFocusOnPressed); void persistentSelectionChanged(bool isPersistentSelection); void textMarginChanged(qreal textMargin); + Q_REVISION(1) void selectByKeyboardChanged(bool selectByKeyboard); void selectByMouseChanged(bool selectByMouse); void mouseSelectionModeChanged(SelectionMode mode); void linkActivated(const QString &link); diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index f65af3d2d30a48bcc9dd50c96a759474d8268e3d..dd0f76f8d93e7fae5745f7136b87fafcf1cbf145 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -87,7 +87,7 @@ public: , documentDirty(true), dirty(false), richText(false), cursorVisible(false), cursorPending(false) , focusOnPress(true), persistentSelection(false), requireImplicitWidth(false) , selectByMouse(false), canPaste(false), canPasteValid(false), hAlignImplicit(true) - , textCached(true), inLayout(false) + , textCached(true), inLayout(false), selectByKeyboard(false), selectByKeyboardSet(false) { } @@ -168,6 +168,8 @@ public: bool hAlignImplicit:1; bool textCached:1; bool inLayout:1; + bool selectByKeyboard:1; + bool selectByKeyboardSet:1; }; QT_END_NAMESPACE diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 4c4a04b29314fd568ae8625f3d9f003989983d63..b9041fb7195a1618d87364dec3f2dfc3b48c2e81 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -134,6 +134,9 @@ private slots: void dragMouseSelection(); void mouseSelectionMode_accessors(); void selectByMouse(); + void selectByKeyboard(); + void keyboardSelection_data(); + void keyboardSelection(); void renderType(); void inputMethodHints(); @@ -2063,6 +2066,127 @@ void tst_qquicktextedit::selectByMouse() QCOMPARE(spy.at(1).at(0).toBool(), false); } +void tst_qquicktextedit::selectByKeyboard() +{ + QQmlComponent oldComponent(&engine); + oldComponent.setData("import QtQuick 2.0\n TextEdit { selectByKeyboard: true }", QUrl()); + QVERIFY(!oldComponent.create()); + + QQmlComponent component(&engine); + component.setData("import QtQuick 2.1\n TextEdit { }", QUrl()); + QScopedPointer<QObject> object(component.create()); + QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object.data()); + QVERIFY(edit); + + QSignalSpy spy(edit, SIGNAL(selectByKeyboardChanged(bool))); + + QCOMPARE(edit->isReadOnly(), false); + QCOMPARE(edit->selectByKeyboard(), true); + + edit->setReadOnly(true); + QCOMPARE(edit->selectByKeyboard(), false); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.at(0).at(0).toBool(), false); + + edit->setSelectByKeyboard(true); + QCOMPARE(edit->selectByKeyboard(), true); + QCOMPARE(spy.count(), 2); + QCOMPARE(spy.at(1).at(0).toBool(), true); + + edit->setReadOnly(false); + QCOMPARE(edit->selectByKeyboard(), true); + QCOMPARE(spy.count(), 2); + + edit->setSelectByKeyboard(false); + QCOMPARE(edit->selectByKeyboard(), false); + QCOMPARE(spy.count(), 3); + QCOMPARE(spy.at(2).at(0).toBool(), false); +} + +Q_DECLARE_METATYPE(QKeySequence::StandardKey) + +void tst_qquicktextedit::keyboardSelection_data() +{ + QTest::addColumn<QString>("text"); + QTest::addColumn<bool>("readOnly"); + QTest::addColumn<bool>("selectByKeyboard"); + QTest::addColumn<int>("cursorPosition"); + QTest::addColumn<QKeySequence::StandardKey>("standardKey"); + QTest::addColumn<QString>("selectedText"); + + QTest::newRow("editable - select first char") + << QStringLiteral("editable - select first char") << false << true << 0 << QKeySequence::SelectNextChar << QStringLiteral("e"); + QTest::newRow("editable - select first word") + << QStringLiteral("editable - select first char") << false << true << 0 << QKeySequence::SelectNextWord << QStringLiteral("editable "); + + QTest::newRow("editable - cannot select first char") + << QStringLiteral("editable - cannot select first char") << false << false << 0 << QKeySequence::SelectNextChar << QStringLiteral(""); + QTest::newRow("editable - cannot select first word") + << QStringLiteral("editable - cannot select first word") << false << false << 0 << QKeySequence::SelectNextWord << QStringLiteral(""); + + QTest::newRow("editable - select last char") + << QStringLiteral("editable - select last char") << false << true << 27 << QKeySequence::SelectPreviousChar << QStringLiteral("r"); + QTest::newRow("editable - select last word") + << QStringLiteral("editable - select last word") << false << true << 27 << QKeySequence::SelectPreviousWord << QStringLiteral("word"); + + QTest::newRow("editable - cannot select last char") + << QStringLiteral("editable - cannot select last char") << false << false << 35 << QKeySequence::SelectPreviousChar << QStringLiteral(""); + QTest::newRow("editable - cannot select last word") + << QStringLiteral("editable - cannot select last word") << false << false << 35 << QKeySequence::SelectPreviousWord << QStringLiteral(""); + + QTest::newRow("read-only - cannot select first char") + << QStringLiteral("read-only - cannot select first char") << true << false << 0 << QKeySequence::SelectNextChar << QStringLiteral(""); + QTest::newRow("read-only - cannot select first word") + << QStringLiteral("read-only - cannot select first word") << true << false << 0 << QKeySequence::SelectNextWord << QStringLiteral(""); + + QTest::newRow("read-only - cannot select last char") + << QStringLiteral("read-only - cannot select last char") << true << false << 35 << QKeySequence::SelectPreviousChar << QStringLiteral(""); + QTest::newRow("read-only - cannot select last word") + << QStringLiteral("read-only - cannot select last word") << true << false << 35 << QKeySequence::SelectPreviousWord << QStringLiteral(""); + + QTest::newRow("read-only - select first char") + << QStringLiteral("read-only - select first char") << true << true << 0 << QKeySequence::SelectNextChar << QStringLiteral("r"); + QTest::newRow("read-only - select first word") + << QStringLiteral("read-only - select first word") << true << true << 0 << QKeySequence::SelectNextWord << QStringLiteral("read"); + + QTest::newRow("read-only - select last char") + << QStringLiteral("read-only - select last char") << true << true << 28 << QKeySequence::SelectPreviousChar << QStringLiteral("r"); + QTest::newRow("read-only - select last word") + << QStringLiteral("read-only - select last word") << true << true << 28 << QKeySequence::SelectPreviousWord << QStringLiteral("word"); +} + +void tst_qquicktextedit::keyboardSelection() +{ + QFETCH(QString, text); + QFETCH(bool, readOnly); + QFETCH(bool, selectByKeyboard); + QFETCH(int, cursorPosition); + QFETCH(QKeySequence::StandardKey, standardKey); + QFETCH(QString, selectedText); + + QQmlComponent component(&engine); + component.setData("import QtQuick 2.1\n TextEdit { focus: true }", QUrl()); + QScopedPointer<QObject> object(component.create()); + QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object.data()); + QVERIFY(edit); + + edit->setText(text); + edit->setReadOnly(readOnly); + edit->setSelectByKeyboard(selectByKeyboard); + edit->setCursorPosition(cursorPosition); + + QQuickWindow window; + edit->setParentItem(window.contentItem()); + window.show(); + window.requestActivate(); + QTest::qWaitForWindowActive(&window); + QVERIFY(edit->hasActiveFocus()); + + simulateKeys(&window, standardKey); + + QCOMPARE(edit->selectedText(), selectedText); +} + void tst_qquicktextedit::renderType() { QQmlComponent component(&engine); diff --git a/tests/testapplications/text/textedit.qml b/tests/testapplications/text/textedit.qml index 789a52894e4c1dba7cac7f98da2c55bf1c3b5672..e0d7dbdde45ba72b13a15b08a427a9fb679efac9 100644 --- a/tests/testapplications/text/textedit.qml +++ b/tests/testapplications/text/textedit.qml @@ -39,7 +39,7 @@ ** ****************************************************************************/ -import QtQuick 2.0 +import QtQuick 2.1 Rectangle { height: 360; width: 640 @@ -75,6 +75,7 @@ Rectangle { wrapMode: { wrapvalue.model.get(wrapvalue.currentIndex).value } smooth: { smoothvalue.model.get(smoothvalue.currentIndex).value } selectByMouse: { mousevalue.model.get(mousevalue.currentIndex).value } + selectByKeyboard: { keyboardvalue.model.get(keyboardvalue.currentIndex).value } onLinkActivated: { bordercolor.border.color = "red" } Rectangle { id: bordercolor; color: "transparent"; border.color: "green"; anchors.fill: parent } } @@ -227,6 +228,10 @@ Rectangle { id: mousevalue controlname: "Mouse" model: ListModel { ListElement { name: "Off"; value: false } ListElement { name: "On"; value: true } } } + ControlView { + id: keyboardvalue + controlname: "Keyboard" + model: ListModel { ListElement { name: "Off"; value: false } ListElement { name: "On"; value: true } } } ControlView { id: halignvalue controlname: "HAlign"