From 889fa1969497adbb3d8a0bbb8ea3b88aa3c39cc5 Mon Sep 17 00:00:00 2001 From: renbin Date: Tue, 24 Sep 2024 16:44:34 +0800 Subject: [PATCH] fix: restore column selection after undo As title. update backward / forward delete; update unit test. Log: Restore column selection after undo. Bug: https://pms.uniontech.com/bug-view-273663.html Influence: column-editing --- src/common/utils.h | 71 ++++--- src/editor/deletebackcommond.cpp | 100 +++++++--- src/editor/deletebackcommond.h | 39 ++-- src/editor/deletetextundocommand.cpp | 14 +- src/editor/deletetextundocommand.h | 14 +- src/editor/dtextedit.cpp | 67 +++++-- src/editor/dtextedit.h | 4 + tests/src/editor/ut_deletebackcommond.cpp | 178 +++++++++++++++++- tests/src/editor/ut_deletetextundocommand.cpp | 8 +- 9 files changed, 391 insertions(+), 104 deletions(-) diff --git a/src/common/utils.h b/src/common/utils.h index 951b3397..8e0b3d2a 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -15,16 +15,20 @@ #include #ifndef SAFE_DELETE -#define SAFE_DELETE(p) if((p)) { delete (p); (p) = nullptr;} +#define SAFE_DELETE(p) \ + if ((p)) { \ + delete (p); \ + (p) = nullptr; \ + } #endif -#define DEEPIN_THEME QString("%1share/deepin-editor/themes/deepin.theme").arg(LINGLONG_PREFIX) -#define DEEPIN_DARK_THEME QString("%1share/deepin-editor/themes/deepin_dark.theme").arg(LINGLONG_PREFIX) -#define DATA_SIZE_1024 1024 -#define TEXT_EIDT_MARK_ALL "MARK_ALL" -#define PROC_MEMINFO_PATH "/proc/meminfo" -#define COPY_CONSUME_MEMORY_MULTIPLE 9 //复制文本时内存占用系数 -#define PASTE_CONSUME_MEMORY_MULTIPLE 7 //粘贴文本时内存占用系数 +#define DEEPIN_THEME QString("%1share/deepin-editor/themes/deepin.theme").arg(LINGLONG_PREFIX) +#define DEEPIN_DARK_THEME QString("%1share/deepin-editor/themes/deepin_dark.theme").arg(LINGLONG_PREFIX) +#define DATA_SIZE_1024 1024 +#define TEXT_EIDT_MARK_ALL "MARK_ALL" +#define PROC_MEMINFO_PATH "/proc/meminfo" +#define COPY_CONSUME_MEMORY_MULTIPLE 9 // 复制文本时内存占用系数 +#define PASTE_CONSUME_MEMORY_MULTIPLE 7 // 粘贴文本时内存占用系数 class Utils { @@ -33,13 +37,13 @@ class Utils * @brief 区间交叉类型 */ enum RegionIntersectType { - ELeft, ///< 活动区间在固定区间左侧 例如 [0, 9] 和 [-5, -1] - ERight, ///< 活动区间在固定区间右侧 例如 [0, 9] 和 [10, 15] + ELeft, ///< 活动区间在固定区间左侧 例如 [0, 9] 和 [-5, -1] + ERight, ///< 活动区间在固定区间右侧 例如 [0, 9] 和 [10, 15] - EIntersectLeft, ///< 活动区间在固定区间左侧存在范围重叠 例如 [0, 9] 和 [-5, 5] - EIntersectRight, ///< 活动区间在固定区间右侧存在范围重叠 例如 [0, 9] 和 [5, 15] - EIntersectOutter, ///< 活动区间包含固定区间 例如 [0, 9] 和 [-10, 10] - EIntersectInner, ///< 活动区间处于固定区间内部 例如 [0, 9] 和 [5, 6] + EIntersectLeft, ///< 活动区间在固定区间左侧存在范围重叠 例如 [0, 9] 和 [-5, 5] + EIntersectRight, ///< 活动区间在固定区间右侧存在范围重叠 例如 [0, 9] 和 [5, 15] + EIntersectOutter, ///< 活动区间包含固定区间 例如 [0, 9] 和 [-10, 10] + EIntersectInner, ///< 活动区间处于固定区间内部 例如 [0, 9] 和 [5, 6] }; /** @@ -50,6 +54,18 @@ class Utils V23, }; + // TODO: annother command type + enum UndoCommandId { + IdDefault = -1, // default QUndoCommand::id() return + IdUnknown = 256, // sa: QTextUndoCommand::Command::Custom + IdInsert, + IdDelete, + + IdColumnEdit = 0x1000, // column edit mark + IdColumnEditInsert = IdColumnEdit | IdInsert, + IdColumnEditDelete = IdColumnEdit | IdDelete, // column editing delete command + }; + static QString getQrcPath(const QString &imageName); static QString getQssPath(const QString &qssName); static QSize getRenderSize(int fontSize, const QString &string); @@ -89,25 +105,25 @@ class Utils 安全考虑,不要全局使用.仅在个别控件中使用 *******************************************************************************/ static void clearChildrenFocus(QObject *objParent); - //清除 控件及子控件所以焦点 梁卫东 2020-09-14 10:34:19 + // 清除 控件及子控件所以焦点 梁卫东 2020-09-14 10:34:19 static void clearChildrenFoucusEx(QWidget *pWidget); - //设置所有控件焦点 梁卫东 2020-09-15 17:55:18 + // 设置所有控件焦点 梁卫东 2020-09-15 17:55:18 static void setChildrenFocus(QWidget *pWidget, Qt::FocusPolicy policy = Qt::StrongFocus); - //根据指定名称获取进程数量 秦浩玲 2021-01-26 + // 根据指定名称获取进程数量 秦浩玲 2021-01-26 static int getProcessCountByName(const char *pstrName); - //批量结束指定名称的进程 秦浩玲 2021-01-26 + // 批量结束指定名称的进程 秦浩玲 2021-01-26 static void killProcessByName(const char *pstrName); - //计算字符串MD5哈希值 秦浩玲 2021-01-28 + // 计算字符串MD5哈希值 秦浩玲 2021-01-28 static QString getStringMD5Hash(const QString &input); - //通过dbus接口从任务栏激活窗口 add by guoshaoyu 2021-04-07 + // 通过dbus接口从任务栏激活窗口 add by guoshaoyu 2021-04-07 static bool activeWindowFromDock(quintptr winId); - //判断是否共享文件夹且只读 + // 判断是否共享文件夹且只读 static bool isShareDirAndReadOnly(const QString &filePath); static float codecConfidenceForData(const QTextCodec *codec, const QByteArray &data, const QLocale::Country &country); - //return system language + // return system language static QString getSystemLan(); // 取得系统版本是否为 V23 static SystemVersion getSystemVersion(); @@ -115,19 +131,20 @@ class Utils static bool isWayland(); static bool isTreeland(); - // 计算换行内容 text: 原始文本内容, nWidth: 一行最大宽度, font:字体大小, nElideRow: 最大显示行数,超出最大行时,中间内容加···省略号显示 + // 计算换行内容 text: 原始文本内容, nWidth: 一行最大宽度, font:字体大小, nElideRow: + // 最大显示行数,超出最大行时,中间内容加···省略号显示 static QString lineFeed(const QString &text, int nWidth, const QFont &font, int nElidedRow = 2); // 判断 [x1, y1] 和 [x2, y2] 区间是否存在交集,返回交集类型 static RegionIntersectType checkRegionIntersect(int x1, int y1, int x2, int y2); // 取得当前文本编辑器支持的编码格式,从文件 :/encodes/encodes.ini 中读取 - static QVector> getSupportEncoding(); + static QVector> getSupportEncoding(); static QStringList getSupportEncodingList(); /** - * @brief libPath 动态库路径 - * @param strlib 路径的字符串 - */ + * @brief libPath 动态库路径 + * @param strlib 路径的字符串 + */ static QString libPath(const QString &strlib); // 加载外部定制库,如ZPD定制库的等 diff --git a/src/editor/deletebackcommond.cpp b/src/editor/deletebackcommond.cpp index ae6ec1e5..4e450e1e 100644 --- a/src/editor/deletebackcommond.cpp +++ b/src/editor/deletebackcommond.cpp @@ -3,8 +3,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "deletebackcommond.h" + #include +#include "dtextedit.h" + DeleteBackCommand::DeleteBackCommand(QTextCursor cursor, QPlainTextEdit *edit) : m_cursor(cursor) , m_edit(edit) @@ -33,33 +36,52 @@ void DeleteBackCommand::redo() m_cursor.setPosition(m_delPos + m_delText.size(), QTextCursor::KeepAnchor); m_cursor.deleteChar(); - // 撤销恢复时光标移回要撤销的位置 + // restore cursor position m_edit->setTextCursor(m_cursor); } -DeleteBackAltCommand::DeleteBackAltCommand(const QList &selections, QPlainTextEdit *edit) - : m_ColumnEditSelections(selections) +/** + @brief Delete column selection with 'Backsapce' or 'Delete' + The \a backward is default delete character direction (if not selected). + The 'Backspace' button is forward, and the 'Delete' button is backward. + */ +DeleteBackAltCommand::DeleteBackAltCommand(const QList &selections, + TextEdit *edit, + bool backward) + : m_columnEditSelections(selections) , m_edit(edit) { - int size = m_ColumnEditSelections.size(); int sum = 0; - for (int i = 0; i < size; i++) { + for (int i = 0; i < m_columnEditSelections.size(); i++) { QString text; - auto cursor = m_ColumnEditSelections[i].cursor; - - if (!cursor.hasSelection() && !cursor.atBlockEnd()) { - cursor.setPosition(cursor.position() + 1, QTextCursor::KeepAnchor); + auto cursor = m_columnEditSelections[i].cursor; + + if (!cursor.hasSelection()) { + // keyboard 'delete' button + if (backward) { + if (!cursor.atEnd()) { + cursor.setPosition(cursor.position() + 1, QTextCursor::KeepAnchor); + } + + } else { + // keyboard 'backspace' button + if (!cursor.atStart()) { + cursor.setPosition(cursor.position() - 1); + cursor.setPosition(cursor.position() + 1, QTextCursor::KeepAnchor); + } + } } text = cursor.selectedText(); if (!text.isEmpty()) { int pos = std::min(cursor.anchor(), cursor.position()); DelNode node; - node.m_cursor = cursor; - node.m_insertPos = pos; - node.m_delPos = pos - sum; - node.m_delText = text; - node.m_id_in_Column = i; + node.cursor = cursor; + node.insertPos = pos; + node.delPos = pos - sum; + node.delText = text; + node.idInColumn = i; + node.leftToRight = cursor.anchor() < cursor.position(); m_deletions.push_back(node); sum += text.size(); @@ -71,36 +93,52 @@ DeleteBackAltCommand::~DeleteBackAltCommand() {} void DeleteBackAltCommand::undo() { - int size = m_deletions.size(); - for (int i = 0; i < size; i++) { - auto cursor = m_deletions[i].m_cursor; - int pos = m_deletions[i].m_insertPos; - QString text = m_deletions[i].m_delText; + for (const DelNode &node : m_deletions) { + auto cursor = node.cursor; + const int pos = node.insertPos; + const QString text = node.delText; cursor.setPosition(pos, QTextCursor::MoveAnchor); cursor.insertText(text); - cursor.setPosition(pos, QTextCursor::MoveAnchor); - int id = m_deletions[i].m_id_in_Column; - - if (0 <= id && id < m_ColumnEditSelections.size()) { - m_ColumnEditSelections[id].cursor = cursor; - m_edit->setTextCursor(cursor); + if (0 <= node.idInColumn && node.idInColumn < m_columnEditSelections.size()) { + const int endPos = pos + text.size(); + if (node.leftToRight) { + cursor.setPosition(pos); + cursor.setPosition(endPos, QTextCursor::KeepAnchor); + } else { + cursor.setPosition(endPos); + cursor.setPosition(pos, QTextCursor::KeepAnchor); + } + m_columnEditSelections[node.idInColumn].cursor = cursor; } } + + if (m_edit && !m_columnEditSelections.isEmpty()) { + m_edit->restoreColumnEditSelection(m_columnEditSelections); + m_edit->setTextCursor(m_columnEditSelections.last().cursor); + } } void DeleteBackAltCommand::redo() { - int size = m_deletions.size(); - - for (int i = 0; i < size; i++) { - auto cursor = m_deletions[i].m_cursor; - int pos = m_deletions[i].m_delPos; - QString text = m_deletions[i].m_delText; + for (int i = 0; i < m_deletions.size(); i++) { + auto cursor = m_deletions[i].cursor; + const int pos = m_deletions[i].delPos; + const QString text = m_deletions[i].delText; cursor.setPosition(pos, QTextCursor::MoveAnchor); for (int j = 0; j < text.size(); j++) { cursor.deleteChar(); } } + + if (m_edit && !m_columnEditSelections.isEmpty()) { + m_edit->restoreColumnEditSelection(m_columnEditSelections); + m_edit->setTextCursor(m_columnEditSelections.last().cursor); + } +} + +int DeleteBackAltCommand::id() const +{ + return Utils::IdColumnEditDelete; } diff --git a/src/editor/deletebackcommond.h b/src/editor/deletebackcommond.h index ee66c12a..5cfab4a4 100644 --- a/src/editor/deletebackcommond.h +++ b/src/editor/deletebackcommond.h @@ -4,18 +4,22 @@ #ifndef DELETEBACKCOMMOND_H #define DELETEBACKCOMMOND_H + #include #include #include #include -// 向后删除单一文字或选中文字的撤销重做 + +class TextEdit; + +// Delete selected text or charactor (if not selected) backward class DeleteBackCommand : public QUndoCommand { public: DeleteBackCommand(QTextCursor cursor, QPlainTextEdit *edit); - virtual ~DeleteBackCommand(); - virtual void undo(); - virtual void redo(); + ~DeleteBackCommand() override; + void undo() override; + void redo() override; private: QTextCursor m_cursor; @@ -26,29 +30,32 @@ class DeleteBackCommand : public QUndoCommand QPlainTextEdit *m_edit; }; -// 列模式下向后删除的撤销重做 +// Delete redo / undo on column editing class DeleteBackAltCommand : public QUndoCommand { public: - DeleteBackAltCommand(const QList &selections, QPlainTextEdit *edit); - virtual ~DeleteBackAltCommand(); - virtual void undo(); - virtual void redo(); + DeleteBackAltCommand(const QList &selections, TextEdit *edit, bool backward = false); + ~DeleteBackAltCommand() override; + void undo() override; + void redo() override; + + int id() const override; public: struct DelNode { - QString m_delText; - int m_delPos; - int m_insertPos; - int m_id_in_Column; - QTextCursor m_cursor; + QString delText; + int delPos; + int insertPos; + int idInColumn; + QTextCursor cursor; + bool leftToRight; // select orientation }; private: - QList m_ColumnEditSelections; + QList m_columnEditSelections; QList m_deletions; - QPlainTextEdit *m_edit; + TextEdit *m_edit; }; #endif // DELETEBACKCOMMOND_H diff --git a/src/editor/deletetextundocommand.cpp b/src/editor/deletetextundocommand.cpp index 74516ff2..a9c14b6a 100644 --- a/src/editor/deletetextundocommand.cpp +++ b/src/editor/deletetextundocommand.cpp @@ -7,7 +7,9 @@ #include #include -DeleteTextUndoCommand::DeleteTextUndoCommand(QTextCursor textcursor, QPlainTextEdit *edit, QUndoCommand *parent) +#include "dtextedit.h" + +DeleteTextUndoCommand::DeleteTextUndoCommand(QTextCursor textcursor, TextEdit *edit, QUndoCommand *parent) : QUndoCommand(parent) , m_edit(edit) , m_textCursor(textcursor) @@ -27,7 +29,7 @@ DeleteTextUndoCommand::DeleteTextUndoCommand(QTextCursor textcursor, QPlainTextE } DeleteTextUndoCommand::DeleteTextUndoCommand(QList &selections, - QPlainTextEdit *edit, + TextEdit *edit, QUndoCommand *parent) : QUndoCommand(parent) , m_edit(edit) @@ -72,6 +74,7 @@ void DeleteTextUndoCommand::undo() } if (m_edit && !m_ColumnEditSelections.isEmpty()) { + m_edit->restoreColumnEditSelection(m_ColumnEditSelections); m_edit->setTextCursor(m_ColumnEditSelections.last().cursor); } } @@ -93,11 +96,18 @@ void DeleteTextUndoCommand::redo() } if (m_edit && !m_ColumnEditSelections.isEmpty()) { + m_edit->restoreColumnEditSelection(m_ColumnEditSelections); m_edit->setTextCursor(m_ColumnEditSelections.last().cursor); } } } +int DeleteTextUndoCommand::id() const +{ + return m_ColumnEditSelections.isEmpty() ? Utils::IdDelete + : Utils::IdColumnEditDelete; +} + DeleteTextUndoCommand2::DeleteTextUndoCommand2(QTextCursor textcursor, QString text, QPlainTextEdit *edit, bool currLine) : m_textCursor(textcursor) , m_sInsertText(text) diff --git a/src/editor/deletetextundocommand.h b/src/editor/deletetextundocommand.h index efd69cb1..3ee0988a 100644 --- a/src/editor/deletetextundocommand.h +++ b/src/editor/deletetextundocommand.h @@ -11,16 +11,20 @@ #include #include +class TextEdit; + class DeleteTextUndoCommand : public QUndoCommand { public: - explicit DeleteTextUndoCommand(QTextCursor textcursor, QPlainTextEdit* edit, QUndoCommand *parent = nullptr); - explicit DeleteTextUndoCommand(QList &selections, QPlainTextEdit* edit, QUndoCommand *parent = nullptr); - virtual void undo(); - virtual void redo(); + explicit DeleteTextUndoCommand(QTextCursor textcursor, TextEdit* edit, QUndoCommand *parent = nullptr); + explicit DeleteTextUndoCommand(QList &selections, TextEdit* edit, QUndoCommand *parent = nullptr); + void undo() override; + void redo() override; + + int id() const override; private: - QPlainTextEdit* m_edit; + TextEdit* m_edit; QTextCursor m_textCursor; QString m_sInsertText; QList m_selectTextList; diff --git a/src/editor/dtextedit.cpp b/src/editor/dtextedit.cpp index a9dd93d0..017bd11f 100644 --- a/src/editor/dtextedit.cpp +++ b/src/editor/dtextedit.cpp @@ -258,9 +258,6 @@ void TextEdit::insertColumnEditTextEx(QString text) QPlainTextEdit::selectAll(); } - for (int i = 0; i < m_altModSelections.size(); i++) { - if (m_altModSelections[i].cursor.hasSelection()) deleteTextEx(m_altModSelections[i].cursor); - } QUndoCommand *pInsertStack = new InsertTextUndoCommand(m_altModSelections, text, this); m_pUndoStack->push(pInsertStack); ensureCursorVisible(); @@ -2558,9 +2555,9 @@ void TextEdit::cut(bool ignoreCheck) if (it != m_altModSelections.end() - 1) data += "\n"; } - //删除有选择 - QUndoCommand *pDeleteStack = new DeleteTextUndoCommand(m_altModSelections, this); - m_pUndoStack->push(pDeleteStack); + // Record the column edit delete command + QUndoCommand *pDeleteStack = new DeleteBackAltCommand(m_altModSelections, this); + m_pUndoStack->push(pDeleteStack); //设置到剪切板 QClipboard *clipboard = QApplication::clipboard(); //获取系统剪贴板指针 @@ -2764,12 +2761,8 @@ void TextEdit::slotDeleteAction(bool checked) } if (m_bIsAltMod && !m_altModSelections.isEmpty()) { - for (int i = 0; i < m_altModSelections.size(); i++) { - if (m_altModSelections[i].cursor.hasSelection()) { - QUndoCommand *pDeleteStack = new DeleteTextUndoCommand(m_altModSelections[i].cursor, this); - m_pUndoStack->push(pDeleteStack); - } - } + QUndoCommand *pDeleteStack = new DeleteBackAltCommand(m_altModSelections, this); + m_pUndoStack->push(pDeleteStack); } else { if (textCursor().hasSelection()) { QUndoCommand *pDeleteStack = new DeleteTextUndoCommand(textCursor(), this); @@ -2923,7 +2916,16 @@ void TextEdit::redo_() return; } + // Get current stack info before redo + const bool needUpdate = refreshUndoRedoColumnStatus(); + m_pUndoStack->redo(); + + if (needUpdate) { + renderAllSelections(); + update(); + } + if (m_pUndoStack->index() == m_lastSaveIndex) { this->m_wrapper->window()->updateModifyStatus(m_sFilePath, false); this->m_wrapper->setTemFile(false); @@ -2939,6 +2941,11 @@ void TextEdit::undo_() m_pUndoStack->undo(); + if (refreshUndoRedoColumnStatus()) { + renderAllSelections(); + update(); + } + // 对撤销栈清空的情况下,有两种文件仍需保留*号(重做无需如下判定) // 1. 备份文件,上次修改之后直接关闭时备份的文件,仍需要提示保存 // 2. 临时文件,上次修改后关闭,撤销操作后文件内容不为空 @@ -7059,7 +7066,7 @@ void TextEdit::keyPressEvent(QKeyEvent *e) QPlainTextEdit::selectAll(); if (m_bIsAltMod && !m_altModSelections.isEmpty()) { - QUndoCommand *pDeleteStack = new DeleteTextUndoCommand(m_altModSelections, this); + QUndoCommand *pDeleteStack = new DeleteBackAltCommand(m_altModSelections, this); m_pUndoStack->push(pDeleteStack); } else { //修改backspace删除,在文档最末尾点击backspace,引起标签栏*出现问题 @@ -7082,7 +7089,7 @@ void TextEdit::keyPressEvent(QKeyEvent *e) QPlainTextEdit::selectAll(); if (m_bIsAltMod && !m_altModSelections.isEmpty()) { - DeleteBackAltCommand *commond = new DeleteBackAltCommand(m_altModSelections, this); + DeleteBackAltCommand *commond = new DeleteBackAltCommand(m_altModSelections, this, true); m_pUndoStack->push(commond); } else { //修改delete删除,在文档最末尾点击delete,引起标签栏*出现问题 @@ -7455,6 +7462,10 @@ bool TextEdit::isComment(const QString &text, int index, const QString &commentT return true; } +void TextEdit::restoreColumnEditSelection(const QList &selections) +{ + m_altModSelections = selections; +} void TextEdit::unCommentSelection() { @@ -8059,6 +8070,34 @@ bool TextEdit::findFoldBlock(int line, QTextBlock &beginBlock, QTextBlock &endBl return 0 == braceDepth; } +/** + @brief Call this function after undo/redo , refresh column edit status. + @return Ture if need update seletion status. + */ +bool TextEdit::refreshUndoRedoColumnStatus() +{ + if (const QUndoCommand *cmd = m_pUndoStack->command(m_pUndoStack->index())) { + const int id = cmd->id(); + const bool columnEdit = (id != Utils::IdDefault) && (Utils::IdColumnEdit & id); + if (columnEdit != m_bIsAltMod) { + // update selection + m_bIsAltMod = columnEdit; + if (!m_bIsAltMod) { + m_altModSelections.clear(); + } + + return true; + } + + // in column editing, update every time. + if (columnEdit) { + return true; + } + } + + return false; +} + /** * @brief 文档内容变更时触发 * 当前用于记录中键更新前后的动作并压入撤销栈,需要注意鼠标中键不会移除字符仅会插入选中的字符。 diff --git a/src/editor/dtextedit.h b/src/editor/dtextedit.h index 231ab9df..26989605 100644 --- a/src/editor/dtextedit.h +++ b/src/editor/dtextedit.h @@ -466,6 +466,8 @@ class TextEdit : public DPlainTextEdit static bool isComment(const QString &text, int index, const QString &commentType); + void restoreColumnEditSelection(const QList &selections); + signals: void clickFindAction(); void clickReplaceAction(); @@ -610,6 +612,8 @@ public slots: // 查找行号line起始的折叠区域 bool findFoldBlock(int line, QTextBlock &beginBlock, QTextBlock &endBlock, QTextBlock &curBlock); + bool refreshUndoRedoColumnStatus(); + private slots: // 文档内容变更时触发 void onTextContentChanged(int from, int charsRemoved, int charsAdded); diff --git a/tests/src/editor/ut_deletebackcommond.cpp b/tests/src/editor/ut_deletebackcommond.cpp index d8eca851..8e1660e1 100644 --- a/tests/src/editor/ut_deletebackcommond.cpp +++ b/tests/src/editor/ut_deletebackcommond.cpp @@ -3,10 +3,15 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "ut_deletebackcommond.h" + +#include +#include + +#include "stub.h" + #include "../src/editor/deletebackcommond.h" #include "../../src/widgets/window.h" -#include "qplaintextedit.h" -#include "qtextcursor.h" + UT_Deletebackcommond::UT_Deletebackcommond() {} TEST(UT_Deletebackcommond_DeleteBackCommand, UT_Deletebackcommond_DeleteBackCommand) @@ -75,7 +80,7 @@ TEST(UT_Deletebackaltcommond_DeleteBackAltCommand, UT_Deletebackaltcommond_Delet list.push_back(sel); list.push_back(sel); - QPlainTextEdit *edit = new QPlainTextEdit; + TextEdit *edit = new TextEdit; DeleteBackAltCommand *com = new DeleteBackAltCommand(list, edit); delete com; @@ -99,7 +104,7 @@ TEST(UT_Deletebackaltcommond_redo, UT_Deletebackaltcommond_redo) list.push_back(sel); list.push_back(sel); DeleteBackAltCommand *commond = new DeleteBackAltCommand(list, edit); - commond->m_deletions = {{"123", 1, 1, 1, cursor}}; + commond->m_deletions = {{"123", 1, 1, 1, cursor, true}}; commond->redo(); window->deleteLater(); @@ -136,7 +141,7 @@ TEST(UT_Deletebackaltcommond_undo, UT_Deletebackaltcommond_undo) com = nullptr; } -TEST(UT_Deletebackaltcommond_MoveCursor, UT_Deletebackaltcommond_MoveCursor) +TEST(UT_Deletebackaltcommond_MoveCursor, MoveCursor_Undo) { Window *window = new Window; EditWrapper *wrapper = window->createEditor(); @@ -176,3 +181,166 @@ TEST(UT_Deletebackaltcommond_MoveCursor, UT_Deletebackaltcommond_MoveCursor) delete com; com = nullptr; } + +void stub_slotCanUndoRedoChanged(bool) +{ + // do nothing +} + +void stub_command_updateModifyStatus(const QString &, bool) +{ + // do nothing +} + +TEST(UT_Deletebackaltcommond_Selection, Selection_ColumnStatus_Restore) +{ + Stub s; + s.set(ADDR(TextEdit, slotCanUndoChanged), stub_slotCanUndoRedoChanged); + s.set(ADDR(TextEdit, slotCanRedoChanged), stub_slotCanUndoRedoChanged); + s.set(ADDR(Window, updateModifyStatus), stub_command_updateModifyStatus); + + Window *window = new Window; + EditWrapper *wrapper = window->createEditor(); + TextEdit *edit = wrapper->textEditor(); + QString text = "123456\nabcdef"; + edit->setPlainText(text); + + QList list; + QTextEdit::ExtraSelection sel; + QTextCursor cursor = edit->textCursor(); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 3); + sel.cursor = cursor; + list.append(sel); + cursor.movePosition(QTextCursor::NextBlock); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 3); + sel.cursor = cursor; + list.append(sel); + + DeleteBackCommand *emptyCommand = new DeleteBackCommand({}, edit); + edit->m_pUndoStack->push(emptyCommand); + + DeleteBackAltCommand *com = new DeleteBackAltCommand(list, edit); + edit->m_pUndoStack->push(com); + + edit->undo_(); + // temp command + edit->undo_(); + + EXPECT_TRUE(edit->m_altModSelections.isEmpty()); + EXPECT_FALSE(edit->m_bIsAltMod); + + // temp command + edit->redo_(); + + EXPECT_TRUE(edit->m_altModSelections.isEmpty()); + EXPECT_FALSE(edit->m_bIsAltMod); + + // delete command + edit->redo_(); + + EXPECT_EQ(edit->m_altModSelections.size(), list.size()); + EXPECT_TRUE(edit->m_bIsAltMod); + + window->deleteLater(); + wrapper->deleteLater(); + edit->deleteLater(); +} + +TEST(UT_Deletebackaltcommond_DelStart, DelStart_TextUpdate_Success) +{ + Stub s; + s.set(ADDR(TextEdit, slotCanUndoChanged), stub_slotCanUndoRedoChanged); + s.set(ADDR(TextEdit, slotCanRedoChanged), stub_slotCanUndoRedoChanged); + s.set(ADDR(Window, updateModifyStatus), stub_command_updateModifyStatus); + + Window *window = new Window; + EditWrapper *wrapper = window->createEditor(); + TextEdit *edit = wrapper->textEditor(); + QString text = "123456\nabcdef"; + edit->setPlainText(text); + + QList list; + QTextEdit::ExtraSelection sel; + QTextCursor cursor = edit->textCursor(); + // at start of block: |123456 + // |abcdef + cursor.movePosition(QTextCursor::Start); + sel.cursor = cursor; + list.append(sel); + + cursor.movePosition(QTextCursor::NextBlock); + sel.cursor = cursor; + list.append(sel); + + // backward delete + DeleteBackAltCommand *comBackward = new DeleteBackAltCommand(list, edit, true); + edit->m_pUndoStack->push(comBackward); + EXPECT_EQ(edit->toPlainText(), QString("23456\nbcdef")); + + edit->m_pUndoStack->undo(); + EXPECT_EQ(edit->toPlainText(), text); + + // forward delete + list[0].cursor.setPosition(0); + list[1].cursor.setPosition(7); + DeleteBackAltCommand *comForward = new DeleteBackAltCommand(list, edit); + edit->m_pUndoStack->push(comForward); + EXPECT_EQ(edit->toPlainText(), QString("123456abcdef")); + + edit->m_pUndoStack->undo(); + EXPECT_EQ(edit->toPlainText(), text); + + window->deleteLater(); + wrapper->deleteLater(); + edit->deleteLater(); +} + +TEST(UT_Deletebackaltcommond_DelEnd, DelEnd_TextUpdate_Success) +{ + Stub s; + s.set(ADDR(TextEdit, slotCanUndoChanged), stub_slotCanUndoRedoChanged); + s.set(ADDR(TextEdit, slotCanRedoChanged), stub_slotCanUndoRedoChanged); + s.set(ADDR(Window, updateModifyStatus), stub_command_updateModifyStatus); + + Window *window = new Window; + EditWrapper *wrapper = window->createEditor(); + TextEdit *edit = wrapper->textEditor(); + QString text = "123456\nabcdef"; + edit->setPlainText(text); + + QList list; + QTextEdit::ExtraSelection sel; + QTextCursor cursor = edit->textCursor(); + // at end of block: 123456| + // abcdef| + cursor.movePosition(QTextCursor::EndOfBlock); + sel.cursor = cursor; + list.append(sel); + + cursor.movePosition(QTextCursor::End); + sel.cursor = cursor; + list.append(sel); + + // forward delete + DeleteBackAltCommand *comForward = new DeleteBackAltCommand(list, edit); + edit->m_pUndoStack->push(comForward); + EXPECT_EQ(edit->toPlainText(), QString("12345\nabcde")); + + edit->m_pUndoStack->undo(); + EXPECT_EQ(edit->toPlainText(), text); + + // backward delete + list[0].cursor.setPosition(6); + list[1].cursor.setPosition(13); + DeleteBackAltCommand *comBackward = new DeleteBackAltCommand(list, edit, true); + edit->m_pUndoStack->push(comBackward); + EXPECT_EQ(edit->toPlainText(), QString("123456abcdef")); + + edit->m_pUndoStack->undo(); + EXPECT_EQ(edit->toPlainText(), text); + + window->deleteLater(); + wrapper->deleteLater(); + edit->deleteLater(); +} diff --git a/tests/src/editor/ut_deletetextundocommand.cpp b/tests/src/editor/ut_deletetextundocommand.cpp index 3c252939..2d9a13da 100644 --- a/tests/src/editor/ut_deletetextundocommand.cpp +++ b/tests/src/editor/ut_deletetextundocommand.cpp @@ -92,8 +92,8 @@ TEST(UT_Deletetextundocommond_undo, UT_Deletetextundocommond_undo) TEST(UT_Deletetextundocommond_undo, undo_withTextCursor_restoreCursor) { - // 撤销后恢复光标位置 - QPlainTextEdit *edit = new QPlainTextEdit; + // Revert the cursor position after undo + TextEdit *edit = new TextEdit; edit->setPlainText("123456789"); QTextCursor cursor = edit->textCursor(); @@ -156,8 +156,8 @@ TEST(UT_Deletetextundocommond_redo, UT_Deletetextundocommond_redo) TEST(UT_Deletetextundocommond_rddo, redo_withTextCursor_changeCursor) { - // 重做后恢复光标位置 - QPlainTextEdit *edit = new QPlainTextEdit; + // Restore the cursor position after redo + TextEdit *edit = new TextEdit; edit->setPlainText("123456789"); QTextCursor cursor = edit->textCursor();