From c5945a1552cdc9c2aea819b0946d41a99f379504 Mon Sep 17 00:00:00 2001 From: liujinchang Date: Fri, 23 Oct 2026 18:22:00 +0800 Subject: [PATCH] fix: merge column-edit undo commands for single-step undo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Root cause: InsertTextUndoCommand lacked id()/mergeWith() overrides, so QUndoStack never merged consecutive single-char column-edit insert commands; each keypress produced a separate undo step 2. Fix: add id() returning kColumnEditInsertCommandId and mergeWith() that appends text when both commands have non-empty m_ColumnEditSelections; fix undo() to delete m_sInsertText.length() chars; fix redo() selection to use m_sInsertText.length() without +1 so merged multi-char text is correctly selected on redo path 3. Impact: column-edit undo now removes entire typed text in one step; redo path also correct after merge; normal text insert unaffected (m_ColumnEditSelections is empty, so mergeWith returns false) Log: fix column-edit undo to remove all typed text in a single undo step Influence: 1. Test column-edit mode: type multiple chars then Ctrl+Z, verify single-step undo 2. Test column-edit redo after undo: Ctrl+Shift+Z then Ctrl+Z again, verify no data corruption 3. Test normal text editing undo/redo remains unchanged 4. Test column-edit with mixed operations (insert, delete, undo, redo) fix: 修复列编辑模式撤销需逐字符撤销的问题 1. 根因:InsertTextUndoCommand 未重写 id()/mergeWith(),QUndoStack 无法合并列编辑模式下连续按键产生的单字符插入命令,每次按键产生 独立的撤销步骤 2. 方案:添加 id() 返回 kColumnEditInsertCommandId 和 mergeWith(), 当双方 m_ColumnEditSelections 均非空时合并文本;修复 undo() 列编辑 分支按 m_sInsertText.length() 删除;修复 redo() 选区计算移除 +1, 确保合并后多字符文本在 redo 路径正确选中 3. 影响:列编辑撤销现在可一步撤销全部输入;redo 路径在合并后同样 正确;普通文本编辑不受影响(m_ColumnEditSelections 为空, mergeWith 返回 false) Log: 修复列编辑模式撤销需逐字符撤销的问题 Influence: 1. 测试列编辑模式:输入多个字符后 Ctrl+Z,验证一步撤销 2. 测试列编辑撤销后重做再撤销:Ctrl+Shift+Z 后 Ctrl+Z,验证无数据损坏 3. 测试普通文本编辑的撤销/重做行为保持不变 4. 测试列编辑混合操作(插入、删除、撤销、重做) PMS: BUG-254289 --- src/editor/inserttextundocommand.cpp | 27 +++++++++++++++++++++++++-- src/editor/inserttextundocommand.h | 3 +++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/editor/inserttextundocommand.cpp b/src/editor/inserttextundocommand.cpp index b02864ff4..3ea8c1b86 100644 --- a/src/editor/inserttextundocommand.cpp +++ b/src/editor/inserttextundocommand.cpp @@ -44,7 +44,12 @@ void InsertTextUndoCommand::undo() } else { int cnt = m_ColumnEditSelections.size(); for (int i = 0; i < cnt; i++) { - m_ColumnEditSelections[i].cursor.deleteChar(); + QTextCursor &cursor = m_ColumnEditSelections[i].cursor; + int endPos = cursor.selectionEnd(); + int startPos = endPos - m_sInsertText.length(); + cursor.setPosition(startPos); + cursor.setPosition(endPos, QTextCursor::KeepAnchor); + cursor.deleteChar(); } if (m_pEdit && !m_ColumnEditSelections.isEmpty()) { @@ -76,7 +81,7 @@ void InsertTextUndoCommand::redo() 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, + m_ColumnEditSelections[i].cursor.setPosition(m_ColumnEditSelections[i].cursor.position() - m_sInsertText.length(), QTextCursor::KeepAnchor); } @@ -88,6 +93,24 @@ void InsertTextUndoCommand::redo() } } +int InsertTextUndoCommand::id() const +{ + return kColumnEditInsertCommandId; +} + +bool InsertTextUndoCommand::mergeWith(const QUndoCommand *other) +{ + if (other->id() != id()) + return false; + + const auto *cmd = static_cast(other); + if (m_ColumnEditSelections.isEmpty() || cmd->m_ColumnEditSelections.isEmpty()) + return false; + + m_sInsertText += cmd->m_sInsertText; + return true; +} + /** * @class MidButtonInsertTextUndoCommand * @brief 用于鼠标中键黏贴的插入撤销项,使用 QClipboard::Selection 类型插入选中的数据, diff --git a/src/editor/inserttextundocommand.h b/src/editor/inserttextundocommand.h index 393dc95c0..7243a5887 100644 --- a/src/editor/inserttextundocommand.h +++ b/src/editor/inserttextundocommand.h @@ -21,6 +21,8 @@ class InsertTextUndoCommand : public QUndoCommand QUndoCommand *parent = nullptr); virtual void undo(); virtual void redo(); + int id() const override; + bool mergeWith(const QUndoCommand *other) override; private: QPlainTextEdit *m_pEdit = nullptr; @@ -30,6 +32,7 @@ class InsertTextUndoCommand : public QUndoCommand QString m_sInsertText; QList m_ColumnEditSelections; QString m_selectText = QString(); + static constexpr int kColumnEditInsertCommandId = 1001; }; /**