diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 1b3e35bcef48c44adbd28ac3aba8153e2a66fe54..4f9475f5830508ef9d51e200dfea3691d17b8203 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -309,12 +309,6 @@ void QQuickTextControlPrivate::setContent(Qt::TextFormat format, const QString & QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection())); QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor))); QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged())); - - // convenience signal forwards - QObject::connect(doc, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool))); - QObject::connect(doc, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool))); - QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool))); - QObject::connect(doc, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int))); } bool previousUndoRedoState = doc->isUndoRedoEnabled(); diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h index 6b4c1a869cf66d9c53b82569742710b587625775..0ea21169c94315b2aad4fe540903e0c18b739a22 100644 --- a/src/quick/items/qquicktextcontrol_p.h +++ b/src/quick/items/qquicktextcontrol_p.h @@ -174,11 +174,9 @@ Q_SIGNALS: void updateCursorRequest(const QRectF &rect = QRectF()); void updateRequest(const QRectF &rect = QRectF()); void documentSizeChanged(const QSizeF &); - void blockCountChanged(int newBlockCount); void cursorRectangleChanged(); void linkActivated(const QString &link); void linkHovered(const QString &); - void modificationChanged(bool m); public: // control properties diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index a11acd7afe8a361f50820f3c6e0f00f0f1698862..eba85828bc4b3ae3260774040bae833ddfb3a858 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -56,6 +56,7 @@ #include <QtCore/qmath.h> #include <private/qdeclarativeglobal_p.h> +#include <private/qdeclarativeproperty_p.h> #include <private/qtextengine_p.h> #include <QtQuick/private/qsgtexture_p.h> #include <private/qsgadaptationlayer_p.h> @@ -1402,6 +1403,29 @@ void QQuickTextEdit::paste() } #endif // QT_NO_CLIPBOARD + +/*! + Undoes the last operation if undo is \l {canUndo}{available}. Deselects any + current selection, and updates the selection start to the current cursor + position. +*/ + +void QQuickTextEdit::undo() +{ + Q_D(QQuickTextEdit); + d->control->undo(); +} + +/*! + Redoes the last operation if redo is \l {canRedo}{available}. +*/ + +void QQuickTextEdit::redo() +{ + Q_D(QQuickTextEdit); + d->control->redo(); +} + /*! \overload Handles the given mouse \a event. @@ -1652,6 +1676,32 @@ bool QQuickTextEdit::canPaste() const return d->canPaste; } +/*! + \qmlproperty bool QtQuick2::TextEdit::canUndo + + Returns true if the TextEdit is writable and there are previous operations + that can be undone. +*/ + +bool QQuickTextEdit::canUndo() const +{ + Q_D(const QQuickTextEdit); + return d->document->isUndoAvailable(); +} + +/*! + \qmlproperty bool QtQuick2::TextEdit::canRedo + + Returns true if the TextEdit is writable and there are \l {undo}{undone} + operations that can be redone. +*/ + +bool QQuickTextEdit::canRedo() const +{ + Q_D(const QQuickTextEdit); + return d->document->isRedoAvailable(); +} + /*! \qmlproperty bool QtQuick2::TextEdit::inputMethodComposing @@ -1709,6 +1759,8 @@ void QQuickTextEditPrivate::init() #ifndef QT_NO_CLIPBOARD QObject::connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged())); #endif + FAST_CONNECT(document, SIGNAL(undoAvailable(bool)), q, SIGNAL(canUndoChanged())); + FAST_CONNECT(document, SIGNAL(redoAvailable(bool)), q, SIGNAL(canRedoChanged())); document->setDefaultFont(font); document->setDocumentMargin(textMargin); diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index ef16cfe1298562271b3b869528db257a9852d202..f37b7cd8ff2d41e929f7ea40f23f61d6ea1cc5af 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -90,6 +90,8 @@ class Q_AUTOTEST_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem 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) + Q_PROPERTY(bool canUndo READ canUndo NOTIFY canUndoChanged) + Q_PROPERTY(bool canRedo READ canRedo NOTIFY canRedoChanged) Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged) public: @@ -193,6 +195,9 @@ public: bool canPaste() const; + bool canUndo() const; + bool canRedo() const; + virtual void componentComplete(); /* FROM EDIT */ @@ -248,6 +253,8 @@ Q_SIGNALS: void mouseSelectionModeChanged(SelectionMode mode); void linkActivated(const QString &link); void canPasteChanged(); + void canUndoChanged(); + void canRedoChanged(); void inputMethodComposingChanged(); void effectiveHorizontalAlignmentChanged(); @@ -262,6 +269,8 @@ public Q_SLOTS: void copy(); void paste(); #endif + void undo(); + void redo(); void insert(int position, const QString &text); void remove(int start, int end); diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 7858345198a76163877b64f6a9de12fee68b8d4f..df536f4f59a48c7383d7891bdf36f03fdea6d354 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -571,6 +571,7 @@ void QQuickTextInput::setReadOnly(bool ro) if (!ro) d->setCursorPosition(d->end()); q_canPasteChanged(); + d->emitUndoRedoChanged(); emit readOnlyChanged(ro); } @@ -1753,6 +1754,34 @@ void QQuickTextInput::paste() } #endif // QT_NO_CLIPBOARD +/*! + Undoes the last operation if undo is \l {canUndo}{available}. Deselects any + current selection, and updates the selection start to the current cursor + position. +*/ + +void QQuickTextInput::undo() +{ + Q_D(QQuickTextInput); + if (!d->m_readOnly) { + d->internalUndo(); + d->finishChange(-1, true); + } +} + +/*! + Redoes the last operation if redo is \l {canRedo}{available}. +*/ + +void QQuickTextInput::redo() +{ + Q_D(QQuickTextInput); + if (!d->m_readOnly) { + d->internalRedo(); + d->finishChange(); + } +} + /*! \qmlmethod void QtQuick2::TextInput::insert(int position, string text) @@ -2043,6 +2072,32 @@ bool QQuickTextInput::canPaste() const return d->canPaste; } +/*! + \qmlproperty bool QtQuick2::TextInput::canUndo + + Returns true if the TextInput is writable and there are previous operations + that can be undone. +*/ + +bool QQuickTextInput::canUndo() const +{ + Q_D(const QQuickTextInput); + return d->canUndo; +} + +/*! + \qmlproperty bool QtQuick2::TextInput::canRedo + + Returns true if the TextInput is writable and there are \l {undo}{undone} + operations that can be redone. +*/ + +bool QQuickTextInput::canRedo() const +{ + Q_D(const QQuickTextInput); + return d->canRedo; +} + void QQuickTextInput::moveCursorSelection(int position) { Q_D(QQuickTextInput); @@ -2988,6 +3043,7 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo notifyInputPanel |= (m_cursor == m_lastCursorPos); if (notifyInputPanel) q->updateMicroFocus(); + emitUndoRedoChanged(); emitCursorPositionChanged(); return true; @@ -3557,7 +3613,6 @@ void QQuickTextInputPrivate::internalUndo(int until) } } m_textDirty = true; - emitCursorPositionChanged(); } void QQuickTextInputPrivate::internalRedo() @@ -3600,7 +3655,21 @@ void QQuickTextInputPrivate::internalRedo() } } m_textDirty = true; - emitCursorPositionChanged(); +} + +void QQuickTextInputPrivate::emitUndoRedoChanged() +{ + Q_Q(QQuickTextInput); + const bool previousUndo = canUndo; + const bool previousRedo = canRedo; + + canUndo = isUndoAvailable(); + canRedo = isRedoAvailable(); + + if (previousUndo != canUndo) + emit q->canUndoChanged(); + if (previousRedo != canRedo) + emit q->canRedoChanged(); } /*! @@ -3725,11 +3794,11 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event) #ifndef QT_NO_SHORTCUT else if (event == QKeySequence::Undo) { if (!m_readOnly) - undo(); + q->undo(); } else if (event == QKeySequence::Redo) { if (!m_readOnly) - redo(); + q->redo(); } else if (event == QKeySequence::SelectAll) { selectAll(); diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index eadd0ccc1be5f0287332f06bdb7df3001fee828e..535b1af266302b3fd8ac0440132a9137d78c643e 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -99,6 +99,8 @@ class Q_AUTOTEST_EXPORT QQuickTextInput : public QQuickImplicitSizeItem 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) + Q_PROPERTY(bool canUndo READ canUndo NOTIFY canUndoChanged) + Q_PROPERTY(bool canRedo READ canRedo NOTIFY canRedoChanged) Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged) public: @@ -238,6 +240,9 @@ public: QRectF boundingRect() const; bool canPaste() const; + bool canUndo() const; + bool canRedo() const; + bool isInputMethodComposing() const; Qt::InputMethodHints imHints() const; @@ -275,6 +280,8 @@ Q_SIGNALS: void selectByMouseChanged(bool selectByMouse); void mouseSelectionModeChanged(SelectionMode mode); void canPasteChanged(); + void canUndoChanged(); + void canRedoChanged(); void inputMethodComposingChanged(); void effectiveHorizontalAlignmentChanged(); @@ -306,6 +313,8 @@ public Q_SLOTS: void copy(); void paste(); #endif + void undo(); + void redo(); void insert(int position, const QString &text); void remove(int start, int end); diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index b20b8c44f2be4e8e3e89f621c783a0244899382b..03d825d108876a75c298e7becb6c6f932079e7de 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -113,6 +113,8 @@ public: , selectByMouse(false) , canPaste(false) , canPasteValid(false) + , canUndo(false) + , canRedo(false) , hAlignImplicit(true) , selectPressed(false) , textLayoutDirty(true) @@ -237,6 +239,8 @@ public: bool selectByMouse:1; bool canPaste:1; bool canPasteValid:1; + bool canUndo:1; + bool canRedo:1; bool hAlignImplicit:1; bool selectPressed:1; bool textLayoutDirty:1; @@ -349,8 +353,6 @@ public: void insert(const QString &); void clear(); - void undo() { internalUndo(); finishChange(-1, true); } - void redo() { internalRedo(); finishChange(); } void selectWordAtPos(int); void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(qMax(0, pos)); } @@ -422,6 +424,7 @@ private: void internalUndo(int until = -1); void internalRedo(); + void emitUndoRedoChanged(); void emitCursorPositionChanged(); diff --git a/tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp index 78bab0aeef152e99538bcc83b392ca618d09932b..7d40bc4168dd5816924a7e9e630385a37261d023 100644 --- a/tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp @@ -3326,28 +3326,32 @@ void tst_qquicktextedit::undo() QFETCH(QStringList, expectedString); QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }"; - QDeclarativeComponent textInputComponent(&engine); - textInputComponent.setData(componentStr.toLatin1(), QUrl()); - QQuickTextEdit *textInput = qobject_cast<QQuickTextEdit*>(textInputComponent.create()); - QVERIFY(textInput != 0); + QDeclarativeComponent textEditComponent(&engine); + textEditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create()); + QVERIFY(textEdit != 0); QQuickCanvas canvas; - textInput->setParentItem(canvas.rootItem()); + textEdit->setParentItem(canvas.rootItem()); canvas.show(); canvas.requestActivateWindow(); QTest::qWaitForWindowShown(&canvas); QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas); + QVERIFY(!textEdit->canUndo()); + + QSignalSpy spy(textEdit, SIGNAL(canUndoChanged())); + int i; // STEP 1: First build up an undo history by inserting or typing some strings... for (i = 0; i < insertString.size(); ++i) { if (insertIndex[i] > -1) - textInput->setCursorPosition(insertIndex[i]); + textEdit->setCursorPosition(insertIndex[i]); // experimental stuff if (insertMode[i] == REPLACE_UNTIL_END) { - textInput->select(insertIndex[i], insertIndex[i] + 8); + textEdit->select(insertIndex[i], insertIndex[i] + 8); // This is what I actually want... // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier); @@ -3357,14 +3361,19 @@ void tst_qquicktextedit::undo() QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1()); } + QCOMPARE(spy.count(), 1); + // STEP 2: Next call undo several times and see if we can restore to the previous state for (i = 0; i < expectedString.size() - 1; ++i) { - QCOMPARE(textInput->text(), expectedString[i]); - simulateKeys(&canvas, QKeySequence::Undo); + QCOMPARE(textEdit->text(), expectedString[i]); + QVERIFY(textEdit->canUndo()); + textEdit->undo(); } // STEP 3: Verify that we have undone everything - QVERIFY(textInput->text().isEmpty()); + QVERIFY(textEdit->text().isEmpty()); + QVERIFY(!textEdit->canUndo()); + QCOMPARE(spy.count(), 2); } void tst_qquicktextedit::redo_data() @@ -3403,35 +3412,53 @@ void tst_qquicktextedit::redo() QFETCH(QStringList, expectedString); QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }"; - QDeclarativeComponent textInputComponent(&engine); - textInputComponent.setData(componentStr.toLatin1(), QUrl()); - QQuickTextEdit *textInput = qobject_cast<QQuickTextEdit*>(textInputComponent.create()); - QVERIFY(textInput != 0); + QDeclarativeComponent textEditComponent(&engine); + textEditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create()); + QVERIFY(textEdit != 0); QQuickCanvas canvas; - textInput->setParentItem(canvas.rootItem()); + textEdit->setParentItem(canvas.rootItem()); canvas.show(); canvas.requestActivateWindow(); QTest::qWaitForWindowShown(&canvas); QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas); + QVERIFY(!textEdit->canUndo()); + QVERIFY(!textEdit->canRedo()); + + QSignalSpy spy(textEdit, SIGNAL(canRedoChanged())); + int i; // inserts the diff strings at diff positions for (i = 0; i < insertString.size(); ++i) { if (insertIndex[i] > -1) - textInput->setCursorPosition(insertIndex[i]); + textEdit->setCursorPosition(insertIndex[i]); for (int j = 0; j < insertString.at(i).length(); j++) QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1()); + QVERIFY(textEdit->canUndo()); + QVERIFY(!textEdit->canRedo()); } + QCOMPARE(spy.count(), 0); + // undo everything - while (!textInput->text().isEmpty()) - simulateKeys(&canvas, QKeySequence::Undo); + while (!textEdit->text().isEmpty()) { + QVERIFY(textEdit->canUndo()); + textEdit->undo(); + QVERIFY(textEdit->canRedo()); + } + + QCOMPARE(spy.count(), 1); for (i = 0; i < expectedString.size(); ++i) { - simulateKeys(&canvas, QKeySequence::Redo); - QCOMPARE(textInput->text() , expectedString[i]); + QVERIFY(textEdit->canRedo()); + textEdit->redo(); + QCOMPARE(textEdit->text() , expectedString[i]); + QVERIFY(textEdit->canUndo()); } + QVERIFY(!textEdit->canRedo()); + QCOMPARE(spy.count(), 2); } void tst_qquicktextedit::undo_keypressevents_data() @@ -3578,13 +3605,13 @@ void tst_qquicktextedit::undo_keypressevents() QFETCH(QStringList, expectedString); QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }"; - QDeclarativeComponent textInputComponent(&engine); - textInputComponent.setData(componentStr.toLatin1(), QUrl()); - QQuickTextEdit *textInput = qobject_cast<QQuickTextEdit*>(textInputComponent.create()); - QVERIFY(textInput != 0); + QDeclarativeComponent textEditComponent(&engine); + textEditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create()); + QVERIFY(textEdit != 0); QQuickCanvas canvas; - textInput->setParentItem(canvas.rootItem()); + textEdit->setParentItem(canvas.rootItem()); canvas.show(); canvas.requestActivateWindow(); QTest::qWaitForWindowShown(&canvas); @@ -3593,10 +3620,10 @@ void tst_qquicktextedit::undo_keypressevents() simulateKeys(&canvas, keys); for (int i = 0; i < expectedString.size(); ++i) { - QCOMPARE(textInput->text() , expectedString[i]); - simulateKeys(&canvas, QKeySequence::Undo); + QCOMPARE(textEdit->text() , expectedString[i]); + textEdit->undo(); } - QVERIFY(textInput->text().isEmpty()); + QVERIFY(textEdit->text().isEmpty()); } void tst_qquicktextedit::emptytags_QTBUG_22058() diff --git a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp index e37e81d13b91735068c80a1b5fa909ec45ee99d8..017ac53b3fc30c88b9b8db69aeea7ecf2aa9c8f1 100644 --- a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp @@ -3975,6 +3975,10 @@ void tst_qquicktextinput::undo() QTest::qWaitForWindowShown(&canvas); QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas); + QVERIFY(!textInput->canUndo()); + + QSignalSpy spy(textInput, SIGNAL(canUndoChanged())); + int i; // STEP 1: First build up an undo history by inserting or typing some strings... @@ -3994,14 +3998,19 @@ void tst_qquicktextinput::undo() QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1()); } + QCOMPARE(spy.count(), 1); + // STEP 2: Next call undo several times and see if we can restore to the previous state for (i = 0; i < expectedString.size() - 1; ++i) { QCOMPARE(textInput->text(), expectedString[i]); - simulateKeys(&canvas, QKeySequence::Undo); + QVERIFY(textInput->canUndo()); + textInput->undo(); } // STEP 3: Verify that we have undone everything QVERIFY(textInput->text().isEmpty()); + QVERIFY(!textInput->canUndo()); + QCOMPARE(spy.count(), 2); } void tst_qquicktextinput::redo_data() @@ -4052,6 +4061,11 @@ void tst_qquicktextinput::redo() QTest::qWaitForWindowShown(&canvas); QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas); + QVERIFY(!textInput->canUndo()); + QVERIFY(!textInput->canRedo()); + + QSignalSpy spy(textInput, SIGNAL(canRedoChanged())); + int i; // inserts the diff strings at diff positions for (i = 0; i < insertString.size(); ++i) { @@ -4059,16 +4073,29 @@ void tst_qquicktextinput::redo() textInput->setCursorPosition(insertIndex[i]); for (int j = 0; j < insertString.at(i).length(); j++) QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1()); + QVERIFY(textInput->canUndo()); + QVERIFY(!textInput->canRedo()); } + QCOMPARE(spy.count(), 0); + // undo everything - while (!textInput->text().isEmpty()) - simulateKeys(&canvas, QKeySequence::Undo); + while (!textInput->text().isEmpty()) { + QVERIFY(textInput->canUndo()); + textInput->undo(); + QVERIFY(textInput->canRedo()); + } + + QCOMPARE(spy.count(), 1); for (i = 0; i < expectedString.size(); ++i) { - simulateKeys(&canvas, QKeySequence::Redo); + QVERIFY(textInput->canRedo()); + textInput->redo(); QCOMPARE(textInput->text() , expectedString[i]); + QVERIFY(textInput->canUndo()); } + QVERIFY(!textInput->canRedo()); + QCOMPARE(spy.count(), 2); } void tst_qquicktextinput::undo_keypressevents_data() @@ -4230,7 +4257,7 @@ void tst_qquicktextinput::undo_keypressevents() for (int i = 0; i < expectedString.size(); ++i) { QCOMPARE(textInput->text() , expectedString[i]); - simulateKeys(&canvas, QKeySequence::Undo); + textInput->undo(); } QVERIFY(textInput->text().isEmpty()); }