Skip to content

Commit

Permalink
fix: cursor pos wrong after column insertion.
Browse files Browse the repository at this point in the history
As title.

Log: Fix column insert text.
  • Loading branch information
rb-union committed Sep 25, 2024
1 parent 889fa19 commit d3b9f49
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 31 deletions.
96 changes: 76 additions & 20 deletions src/editor/inserttextundocommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -15,20 +19,29 @@ InsertTextUndoCommand::InsertTextUndoCommand(const QTextCursor &textcursor, cons

InsertTextUndoCommand::InsertTextUndoCommand(QList<QTextEdit::ExtraSelection> &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();
Expand All @@ -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();
Expand All @@ -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 类型插入选中的数据,
Expand Down
28 changes: 22 additions & 6 deletions src/editor/inserttextundocommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,40 @@
#include <QTextEdit>
#include <QPlainTextEdit>

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<QTextEdit::ExtraSelection> &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<QTextEdit::ExtraSelection> m_ColumnEditSelections;
QList<QTextEdit::ExtraSelection> m_columnEditSelections;
QList<ColumnReplaceNode> m_replaces;
QString m_selectText = QString();
};

Expand Down
19 changes: 14 additions & 5 deletions tests/src/editor/ut_inserttextundocommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -137,17 +145,18 @@ 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();

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)
Expand Down

0 comments on commit d3b9f49

Please sign in to comment.