refactor edit operations

- Ctrl+B/I/K/D/M: Revert if content between the markers is empty.
- Ctrl+7: delete title mark.
This commit is contained in:
Le Tan 2017-11-24 19:56:25 +08:00
parent ce48cdafd6
commit 1f0ee88770
6 changed files with 105 additions and 22 deletions

View File

@ -17,6 +17,8 @@ VNote supports `Ctrl+J` and `Ctrl+K` for navigation in the notebooks list, direc
Scroll in all directions. Scroll in all directions.
- `Ctrl+Shift+T` - `Ctrl+Shift+T`
Recover last closed file. Recover last closed file.
- `Ctrl+Alt+L`
Open Flash Page.
### Read Mode ### Read Mode
- `Ctrl+W` - `Ctrl+W`
@ -70,6 +72,8 @@ Delete all the characters from current cursor to the first space backward.
Delete all the characters from current cursor to the beginning of current line. Delete all the characters from current cursor to the beginning of current line.
- `Ctrl+<Num>` - `Ctrl+<Num>`
Insert title at level `<Num>`. `<Num>` should be 1 to 6. Current selected text will be changed to title if exists. Insert title at level `<Num>`. `<Num>` should be 1 to 6. Current selected text will be changed to title if exists.
- `Ctrl+7`
Delete the title mark of current line or selected text.
- `Tab`/`Shift+Tab` - `Tab`/`Shift+Tab`
Increase or decrease the indentation. If any text is selected, the indentation will operate on all these selected lines. Increase or decrease the indentation. If any text is selected, the indentation will operate on all these selected lines.
- `Shift+Enter` - `Shift+Enter`

View File

@ -17,6 +17,8 @@
任意滚动。 任意滚动。
- `Ctrl+Shift+T` - `Ctrl+Shift+T`
恢复上一个关闭的文件。 恢复上一个关闭的文件。
- `Ctrl+Alt+L`
打开灵犀页。
### 阅读模式 ### 阅读模式
- `Ctrl+W` - `Ctrl+W`
@ -70,6 +72,8 @@
删除光标位置到行首的所有字符。 删除光标位置到行首的所有字符。
- `Ctrl+<Num>` - `Ctrl+<Num>`
插入级别为`<Num>`的标题。`<Num>`应该是1到6的一个数字。如果已经选择文本则将当前选择文本改为标题。 插入级别为`<Num>`的标题。`<Num>`应该是1到6的一个数字。如果已经选择文本则将当前选择文本改为标题。
- `Ctrl+7`
删除当前行或所选择文本的标题标记。
- `Tab`/`Shift+Tab` - `Tab`/`Shift+Tab`
增加或减小缩进。如果已经选择文本,则对所有选择的行进行缩进操作。 增加或减小缩进。如果已经选择文本,则对所有选择的行进行缩进操作。
- `Shift+Enter` - `Shift+Enter`

View File

@ -785,7 +785,7 @@ void VEditUtils::insertTitleMark(QTextCursor &p_cursor,
return; return;
} }
Q_ASSERT(p_level >= 1 && p_level <= 6); Q_ASSERT(p_level >= 0 && p_level <= 6);
bool needInsert = true; bool needInsert = true;
@ -801,15 +801,24 @@ void VEditUtils::insertTitleMark(QTextCursor &p_cursor,
needInsert = false; needInsert = false;
} else { } else {
// Remove the title mark. // Remove the title mark.
int length = level;
if (p_level == 0) {
// Remove all the prefix.
QRegExp prefixReg(VUtils::c_headerPrefixRegExp);
bool preMatched = prefixReg.exactMatch(text);
Q_ASSERT(preMatched);
length = prefixReg.cap(1).length();
}
p_cursor.movePosition(QTextCursor::NextCharacter, p_cursor.movePosition(QTextCursor::NextCharacter,
QTextCursor::KeepAnchor, QTextCursor::KeepAnchor,
level); length);
p_cursor.removeSelectedText(); p_cursor.removeSelectedText();
} }
} }
// Insert titleMark + " " at the front of the block. // Insert titleMark + " " at the front of the block.
if (needInsert) { if (p_level > 0 && needInsert) {
// Remove the spaces at front. // Remove the spaces at front.
// insertText() will remove the selection. // insertText() will remove the selection.
moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::KeepAnchor); moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::KeepAnchor);

View File

@ -151,6 +151,7 @@ public:
// Insert title Mark at level @p_level in front of block @p_block // Insert title Mark at level @p_level in front of block @p_block
// If there already exists title marks, remove it first. // If there already exists title marks, remove it first.
// Move cursor at the end of the block after insertion. // Move cursor at the end of the block after insertion.
// If @p_level is 0, remove the title mark.
static void insertTitleMark(QTextCursor &p_cursor, static void insertTitleMark(QTextCursor &p_cursor,
const QTextBlock &p_block, const QTextBlock &p_block,
int p_level); int p_level);

View File

@ -219,10 +219,11 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_4: case Qt::Key_4:
case Qt::Key_5: case Qt::Key_5:
case Qt::Key_6: case Qt::Key_6:
case Qt::Key_7:
{ {
if (modifiers == Qt::ControlModifier) { if (modifiers == Qt::ControlModifier) {
// Ctrl + <N>: insert title at level <N>. // Ctrl + <N>: insert title at level <N>.
if (insertTitle(key - Qt::Key_0)) { if (insertTitle(key == Qt::Key_7 ? 0 : key - Qt::Key_0)) {
p_event->accept(); p_event->accept();
ret = true; ret = true;
goto exit; goto exit;
@ -728,18 +729,32 @@ void VMdEditOperations::decorateBold()
cursor.insertText("**"); cursor.insertText("**");
} else { } else {
// Insert **** and place cursor in the middle. // Insert **** and place cursor in the middle.
// Or if there are two * after current cursor, just skip them. // Or if there are two * after current cursor, just skip them or delete
// them if four * appear.
int pos = cursor.positionInBlock(); int pos = cursor.positionInBlock();
bool hasStars = false; bool hasStars = false;
bool emptyMarkers = false;
QString text = cursor.block().text(); QString text = cursor.block().text();
if (pos <= text.size() - 2) { if (pos <= text.size() - 2) {
if (text[pos] == '*' && text[pos + 1] == '*') { if (text[pos] == '*' && text[pos + 1] == '*') {
hasStars = true; hasStars = true;
if (pos >= 2
&& text[pos - 1] == '*'
&& text[pos - 2] == '*') {
emptyMarkers = true;
}
} }
} }
if (hasStars) { if (hasStars) {
if (emptyMarkers) {
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 2);
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
cursor.removeSelectedText();
} else {
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
}
} else { } else {
cursor.insertText("****"); cursor.insertText("****");
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2); cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
@ -765,18 +780,31 @@ void VMdEditOperations::decorateItalic()
cursor.insertText("*"); cursor.insertText("*");
} else { } else {
// Insert ** and place cursor in the middle. // Insert ** and place cursor in the middle.
// Or if there are one * after current cursor, just skip it. // Or if there are one * after current cursor, just skip them or delete
// them if two * appear.
int pos = cursor.positionInBlock(); int pos = cursor.positionInBlock();
bool hasStar = false; bool hasStar = false;
bool emptyMarkers = false;
QString text = cursor.block().text(); QString text = cursor.block().text();
if (pos <= text.size() - 1) { if (pos <= text.size() - 1) {
if (text[pos] == '*') { if (text[pos] == '*'
&& (pos == text.size() - 1 || text[pos + 1] != '*')) {
hasStar = true; hasStar = true;
if (pos >= 1 && text[pos - 1] == '*') {
emptyMarkers = true;
}
} }
} }
if (hasStar) { if (hasStar) {
if (emptyMarkers) {
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 1);
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
cursor.removeSelectedText();
} else {
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
}
} else { } else {
cursor.insertText("**"); cursor.insertText("**");
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1); cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
@ -802,18 +830,30 @@ void VMdEditOperations::decorateInlineCode()
cursor.insertText("`"); cursor.insertText("`");
} else { } else {
// Insert `` and place cursor in the middle. // Insert `` and place cursor in the middle.
// Or if there are one ` after current cursor, just skip it. // Or if there are one ` after current cursor, just skip them or delete
// them if two ` appear.
int pos = cursor.positionInBlock(); int pos = cursor.positionInBlock();
bool hasBackquote = false; bool hasBackquote = false;
bool emptyMarkers = false;
QString text = cursor.block().text(); QString text = cursor.block().text();
if (pos <= text.size() - 1) { if (pos <= text.size() - 1) {
if (text[pos] == '`') { if (text[pos] == '`') {
hasBackquote = true; hasBackquote = true;
if (pos >= 1 && text[pos - 1] == '`') {
emptyMarkers = true;
}
} }
} }
if (hasBackquote) { if (hasBackquote) {
if (emptyMarkers) {
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 1);
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
cursor.removeSelectedText();
} else {
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
}
} else { } else {
cursor.insertText("``"); cursor.insertText("``");
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1); cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
@ -857,23 +897,33 @@ void VMdEditOperations::decorateCodeBlock()
|| state == HighlightBlockState::CodeBlockStart || state == HighlightBlockState::CodeBlockStart
|| state == HighlightBlockState::CodeBlockEnd) { || state == HighlightBlockState::CodeBlockEnd) {
// Find the block end. // Find the block end.
while (block.isValid()) { QTextBlock endBlock = block;
if (block.userState() == HighlightBlockState::CodeBlockEnd) { while (endBlock.isValid()) {
if (endBlock.userState() == HighlightBlockState::CodeBlockEnd) {
break; break;
} }
block = block.next(); endBlock = endBlock.next();
} }
if (block.isValid()) { if (endBlock.isValid()) {
// It is CodeBlockEnd. // It is CodeBlockEnd.
cursor.setPosition(block.position()); if (endBlock.previous().isValid()
if (block.next().isValid()) { && endBlock.previous().userState() == HighlightBlockState::CodeBlockStart) {
// Delete empty code blocks.
cursor.setPosition(endBlock.previous().position());
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
} else {
cursor.setPosition(endBlock.position());
if (endBlock.next().isValid()) {
cursor.movePosition(QTextCursor::NextBlock); cursor.movePosition(QTextCursor::NextBlock);
cursor.movePosition(QTextCursor::StartOfBlock); cursor.movePosition(QTextCursor::StartOfBlock);
} else { } else {
cursor.movePosition(QTextCursor::EndOfBlock); cursor.movePosition(QTextCursor::EndOfBlock);
} }
}
} else { } else {
// Reach the end of the document. // Reach the end of the document.
cursor.movePosition(QTextCursor::End); cursor.movePosition(QTextCursor::End);
@ -919,18 +969,32 @@ void VMdEditOperations::decorateStrikethrough()
cursor.insertText("~~"); cursor.insertText("~~");
} else { } else {
// Insert ~~~~ and place cursor in the middle. // Insert ~~~~ and place cursor in the middle.
// Or if there are one ~~ after current cursor, just skip it. // Or if there are one ~~ after current cursor, just skip it or delete
// it if for ~ appear.
int pos = cursor.positionInBlock(); int pos = cursor.positionInBlock();
bool hasStrikethrough = false; bool hasStrikethrough = false;
bool emptyMarkers = false;
QString text = cursor.block().text(); QString text = cursor.block().text();
if (pos <= text.size() - 2) { if (pos <= text.size() - 2) {
if (text[pos] == '~' && text[pos + 1] == '~') { if (text[pos] == '~' && text[pos + 1] == '~') {
hasStrikethrough = true; hasStrikethrough = true;
if (pos >= 2
&& text[pos - 1] == '~'
&& text[pos - 2] == '~') {
emptyMarkers = true;
}
} }
} }
if (hasStrikethrough) { if (hasStrikethrough) {
if (emptyMarkers) {
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 2);
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
cursor.removeSelectedText();
} else {
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
}
} else { } else {
cursor.insertText("~~~~"); cursor.insertText("~~~~");
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2); cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);

View File

@ -58,6 +58,7 @@ private:
// Insert title of level @p_level. // Insert title of level @p_level.
// Will detect if current block already has some leading #s. If yes, // Will detect if current block already has some leading #s. If yes,
// will delete it and insert the correct #s. // will delete it and insert the correct #s.
// @p_level: 0 to cancel title.
bool insertTitle(int p_level); bool insertTitle(int p_level);
// Change the sequence number of a list block. // Change the sequence number of a list block.