diff --git a/src/editor/inserttextundocommand.cpp b/src/editor/inserttextundocommand.cpp index b02864ff..18534715 100644 --- a/src/editor/inserttextundocommand.cpp +++ b/src/editor/inserttextundocommand.cpp @@ -3,8 +3,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "inserttextundocommand.h" +#include "dtextedit.h" -InsertTextUndoCommand::InsertTextUndoCommand(const QTextCursor &textcursor, const QString &text, QPlainTextEdit *edit, QUndoCommand *parent) +InsertTextUndoCommand::InsertTextUndoCommand(const QTextCursor &textcursor, + const QString &text, + TextEdit *edit, + QUndoCommand *parent) : QUndoCommand(parent) , m_pEdit(edit) , m_textCursor(textcursor) @@ -15,20 +19,29 @@ InsertTextUndoCommand::InsertTextUndoCommand(const QTextCursor &textcursor, cons InsertTextUndoCommand::InsertTextUndoCommand(QList &selections, const QString &text, - QPlainTextEdit *edit, + TextEdit *edit, QUndoCommand *parent) : QUndoCommand(parent) , m_pEdit(edit) , m_sInsertText(text) - , m_ColumnEditSelections(selections) + , m_columnEditSelections(selections) { m_sInsertText.replace("\r\n", "\n"); + + for (const auto &selection : m_columnEditSelections) { + ColumnReplaceNode replaceNode; + replaceNode.startPos = selection.cursor.selectionStart(); + replaceNode.endPos = selection.cursor.selectionEnd(); + replaceNode.leftToRight = selection.cursor.anchor() <= selection.cursor.position(); + replaceNode.originText = selection.cursor.selectedText(); + m_replaces.append(replaceNode); + } } void InsertTextUndoCommand::undo() { - if (m_ColumnEditSelections.isEmpty()) { - // 注意部分字符显示占位超过1 + if (m_columnEditSelections.isEmpty()) { + // note that some characters appear to occupy more than 1 place m_textCursor.setPosition(m_endPostion); m_textCursor.setPosition(m_beginPostion, QTextCursor::KeepAnchor); m_textCursor.deleteChar(); @@ -37,25 +50,44 @@ void InsertTextUndoCommand::undo() m_textCursor.setPosition(m_textCursor.position() - m_selectText.length(), QTextCursor::KeepAnchor); } - // 进行撤销/恢复时将光标移动到撤销位置 + // restore cursor position if (m_pEdit) { m_pEdit->setTextCursor(m_textCursor); } } else { - int cnt = m_ColumnEditSelections.size(); - for (int i = 0; i < cnt; i++) { - m_ColumnEditSelections[i].cursor.deleteChar(); + Q_ASSERT_X(m_columnEditSelections.size() == m_replaces.size(), "Column editing insert undo", "column data size check"); + + for (int i = 0; i < m_columnEditSelections.size(); i++) { + QTextEdit::ExtraSelection &selection = m_columnEditSelections[i]; + const ColumnReplaceNode &replaceNode = m_replaces[i]; + + // restore origin text + QTextCursor cursor = selection.cursor; + cursor.setPosition(replaceNode.startPos); + cursor.setPosition(replaceNode.startPos + m_sInsertText.size(), QTextCursor::KeepAnchor); + cursor.insertText(replaceNode.originText); + + if (replaceNode.leftToRight) { + cursor.setPosition(replaceNode.startPos); + cursor.setPosition(replaceNode.endPos, QTextCursor::KeepAnchor); + } else { + cursor.setPosition(replaceNode.endPos); + cursor.setPosition(replaceNode.startPos, QTextCursor::KeepAnchor); + } + + selection.cursor = cursor; } - if (m_pEdit && !m_ColumnEditSelections.isEmpty()) { - m_pEdit->setTextCursor(m_ColumnEditSelections.last().cursor); + if (m_pEdit && !m_columnEditSelections.isEmpty()) { + m_pEdit->restoreColumnEditSelection(m_columnEditSelections); + m_pEdit->setTextCursor(m_columnEditSelections.last().cursor); } } } void InsertTextUndoCommand::redo() { - if (m_ColumnEditSelections.isEmpty()) { + if (m_columnEditSelections.isEmpty()) { if (m_textCursor.hasSelection()) { m_selectText = m_textCursor.selectedText(); m_textCursor.removeSelectedText(); @@ -66,28 +98,52 @@ void InsertTextUndoCommand::redo() m_beginPostion = m_textCursor.selectionStart(); m_endPostion = m_textCursor.selectionEnd(); - // 进行撤销/恢复时将光标移动到撤销位置 + // restore cursor position if (m_pEdit) { QTextCursor curCursor = m_pEdit->textCursor(); curCursor.setPosition(m_endPostion); m_pEdit->setTextCursor(curCursor); } } else { - int cnt = m_ColumnEditSelections.size(); - for (int i = 0; i < cnt; i++) { - m_ColumnEditSelections[i].cursor.insertText(m_sInsertText); - m_ColumnEditSelections[i].cursor.setPosition(m_ColumnEditSelections[i].cursor.position() - m_sInsertText.length() + 1, - QTextCursor::KeepAnchor); + Q_ASSERT_X(m_columnEditSelections.size() == m_replaces.size(), "Column editing insert undo", "column data size check"); + + // insert will change all text cursor + int columnOffset = 0; + for (int i = 0; i < m_columnEditSelections.size(); i++) { + QTextEdit::ExtraSelection &selection = m_columnEditSelections[i]; + const ColumnReplaceNode &replaceNode = m_replaces[i]; + + // remove origin text with replace text + QTextCursor cursor = selection.cursor; + cursor.setPosition(columnOffset + replaceNode.startPos); + cursor.setPosition(columnOffset + replaceNode.endPos, QTextCursor::KeepAnchor); + cursor.insertText(m_sInsertText); + + if (replaceNode.leftToRight) { + cursor.setPosition(columnOffset + replaceNode.startPos); + cursor.setPosition(columnOffset + replaceNode.startPos + m_sInsertText.size(), QTextCursor::KeepAnchor); + } else { + cursor.setPosition(columnOffset + replaceNode.startPos + m_sInsertText.size()); + cursor.setPosition(columnOffset + replaceNode.startPos, QTextCursor::KeepAnchor); + } + + selection.cursor = cursor; + columnOffset += m_sInsertText.size() - replaceNode.originText.size(); } - if (m_pEdit && !m_ColumnEditSelections.isEmpty()) { + if (m_pEdit && !m_columnEditSelections.isEmpty()) { QTextCursor curCursor = m_pEdit->textCursor(); - curCursor.setPosition(m_ColumnEditSelections.last().cursor.selectionEnd()); + curCursor.setPosition(m_columnEditSelections.last().cursor.selectionEnd()); m_pEdit->setTextCursor(curCursor); } } } +int InsertTextUndoCommand::id() const +{ + return m_columnEditSelections.isEmpty() ? Utils::IdColumnEditInsert : Utils::IdInsert; +} + /** * @class MidButtonInsertTextUndoCommand * @brief 用于鼠标中键黏贴的插入撤销项,使用 QClipboard::Selection 类型插入选中的数据, diff --git a/src/editor/inserttextundocommand.h b/src/editor/inserttextundocommand.h index 393dc95c..470e43f0 100644 --- a/src/editor/inserttextundocommand.h +++ b/src/editor/inserttextundocommand.h @@ -11,24 +11,40 @@ #include #include +class TextEdit; + class InsertTextUndoCommand : public QUndoCommand { public: - explicit InsertTextUndoCommand(const QTextCursor &textcursor, const QString &text, QPlainTextEdit *edit, QUndoCommand *parent = nullptr); + explicit InsertTextUndoCommand(const QTextCursor &textcursor, + const QString &text, + TextEdit *edit, + QUndoCommand *parent = nullptr); explicit InsertTextUndoCommand(QList &selections, const QString &text, - QPlainTextEdit *edit, + TextEdit *edit, QUndoCommand *parent = nullptr); - virtual void undo(); - virtual void redo(); + void undo() override; + void redo() override; + + int id() const override; private: - QPlainTextEdit *m_pEdit = nullptr; + struct ColumnReplaceNode + { + int startPos{false}; + int endPos{false}; + bool leftToRight{true}; + QString originText; // replaced text before insert. + }; + + TextEdit *m_pEdit = nullptr; QTextCursor m_textCursor; int m_beginPostion{0}; int m_endPostion{0}; QString m_sInsertText; - QList m_ColumnEditSelections; + QList m_columnEditSelections; + QList m_replaces; QString m_selectText = QString(); }; diff --git a/tests/src/editor/ut_inserttextundocommand.cpp b/tests/src/editor/ut_inserttextundocommand.cpp index 04ddc944..e1efc50e 100644 --- a/tests/src/editor/ut_inserttextundocommand.cpp +++ b/tests/src/editor/ut_inserttextundocommand.cpp @@ -4,6 +4,8 @@ #include "ut_inserttextundocommand.h" #include "../../src/editor/inserttextundocommand.h" +#include "../../src/editor/dtextedit.h" +#include "../../src/widgets/window.h" test_InsertTextUndoCommand::test_InsertTextUndoCommand() { @@ -82,7 +84,9 @@ TEST_F(test_InsertTextUndoCommand, redo2) TEST_F(test_InsertTextUndoCommand, redo_withTextCursor_restoreCursor) { // 重做恢复光标位置 - QPlainTextEdit *edit = new QPlainTextEdit; + Window *window = new Window; + EditWrapper *wrapper = window->createEditor(); + TextEdit *edit = wrapper->textEditor(); edit->setPlainText("123789"); QTextCursor cursor = edit->textCursor(); @@ -108,15 +112,19 @@ TEST_F(test_InsertTextUndoCommand, redo_withTextCursor_restoreCursor) EXPECT_EQ(QString("123456789"), edit->toPlainText()); EXPECT_EQ(6, edit->textCursor().position()); + window->deleteLater(); + wrapper->deleteLater(); + edit->deleteLater(); delete command; delete command2; - delete edit; } TEST_F(test_InsertTextUndoCommand, undo_withTextCursor_restoreCursor) { // 撤销后恢复光标位置 - QPlainTextEdit *edit = new QPlainTextEdit; + Window *window = new Window; + EditWrapper *wrapper = window->createEditor(); + TextEdit *edit = wrapper->textEditor(); edit->setPlainText("123456789"); QTextCursor cursor = edit->textCursor(); @@ -137,7 +145,6 @@ TEST_F(test_InsertTextUndoCommand, undo_withTextCursor_restoreCursor) selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = edit->textCursor(); selection.cursor.setPosition(3); - selection.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 3); extraSelections.append(selection); InsertTextUndoCommand *command2 = new InsertTextUndoCommand(extraSelections, QString("456"), edit); command2->undo(); @@ -145,9 +152,11 @@ TEST_F(test_InsertTextUndoCommand, undo_withTextCursor_restoreCursor) EXPECT_EQ(QString("123789"), edit->toPlainText()); EXPECT_EQ(3, edit->textCursor().position()); + window->deleteLater(); + wrapper->deleteLater(); + edit->deleteLater(); delete command; delete command2; - delete edit; } TEST(test_MidButtonInsertTextUndoCommand, redo_withTextCursor_restoreCursor)