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.
- `Ctrl+Shift+T`
Recover last closed file.
- `Ctrl+Alt+L`
Open Flash Page.
### Read Mode
- `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.
- `Ctrl+<Num>`
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`
Increase or decrease the indentation. If any text is selected, the indentation will operate on all these selected lines.
- `Shift+Enter`

View File

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

View File

@ -785,7 +785,7 @@ void VEditUtils::insertTitleMark(QTextCursor &p_cursor,
return;
}
Q_ASSERT(p_level >= 1 && p_level <= 6);
Q_ASSERT(p_level >= 0 && p_level <= 6);
bool needInsert = true;
@ -801,15 +801,24 @@ void VEditUtils::insertTitleMark(QTextCursor &p_cursor,
needInsert = false;
} else {
// 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,
QTextCursor::KeepAnchor,
level);
length);
p_cursor.removeSelectedText();
}
}
// Insert titleMark + " " at the front of the block.
if (needInsert) {
if (p_level > 0 && needInsert) {
// Remove the spaces at front.
// insertText() will remove the selection.
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
// If there already exists title marks, remove it first.
// 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,
const QTextBlock &p_block,
int p_level);

View File

@ -219,10 +219,11 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_4:
case Qt::Key_5:
case Qt::Key_6:
case Qt::Key_7:
{
if (modifiers == Qt::ControlModifier) {
// 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();
ret = true;
goto exit;
@ -728,18 +729,32 @@ void VMdEditOperations::decorateBold()
cursor.insertText("**");
} else {
// 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();
bool hasStars = false;
bool emptyMarkers = false;
QString text = cursor.block().text();
if (pos <= text.size() - 2) {
if (text[pos] == '*' && text[pos + 1] == '*') {
hasStars = true;
if (pos >= 2
&& text[pos - 1] == '*'
&& text[pos - 2] == '*') {
emptyMarkers = true;
}
}
}
if (hasStars) {
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
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);
}
} else {
cursor.insertText("****");
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
@ -765,18 +780,31 @@ void VMdEditOperations::decorateItalic()
cursor.insertText("*");
} else {
// 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();
bool hasStar = false;
bool emptyMarkers = false;
QString text = cursor.block().text();
if (pos <= text.size() - 1) {
if (text[pos] == '*') {
if (text[pos] == '*'
&& (pos == text.size() - 1 || text[pos + 1] != '*')) {
hasStar = true;
if (pos >= 1 && text[pos - 1] == '*') {
emptyMarkers = true;
}
}
}
if (hasStar) {
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
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);
}
} else {
cursor.insertText("**");
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
@ -802,18 +830,30 @@ void VMdEditOperations::decorateInlineCode()
cursor.insertText("`");
} else {
// 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();
bool hasBackquote = false;
bool emptyMarkers = false;
QString text = cursor.block().text();
if (pos <= text.size() - 1) {
if (text[pos] == '`') {
hasBackquote = true;
if (pos >= 1 && text[pos - 1] == '`') {
emptyMarkers = true;
}
}
}
if (hasBackquote) {
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
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);
}
} else {
cursor.insertText("``");
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
@ -857,22 +897,32 @@ void VMdEditOperations::decorateCodeBlock()
|| state == HighlightBlockState::CodeBlockStart
|| state == HighlightBlockState::CodeBlockEnd) {
// Find the block end.
while (block.isValid()) {
if (block.userState() == HighlightBlockState::CodeBlockEnd) {
QTextBlock endBlock = block;
while (endBlock.isValid()) {
if (endBlock.userState() == HighlightBlockState::CodeBlockEnd) {
break;
}
block = block.next();
endBlock = endBlock.next();
}
if (block.isValid()) {
if (endBlock.isValid()) {
// It is CodeBlockEnd.
cursor.setPosition(block.position());
if (block.next().isValid()) {
cursor.movePosition(QTextCursor::NextBlock);
cursor.movePosition(QTextCursor::StartOfBlock);
if (endBlock.previous().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.movePosition(QTextCursor::EndOfBlock);
cursor.setPosition(endBlock.position());
if (endBlock.next().isValid()) {
cursor.movePosition(QTextCursor::NextBlock);
cursor.movePosition(QTextCursor::StartOfBlock);
} else {
cursor.movePosition(QTextCursor::EndOfBlock);
}
}
} else {
// Reach the end of the document.
@ -919,18 +969,32 @@ void VMdEditOperations::decorateStrikethrough()
cursor.insertText("~~");
} else {
// 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();
bool hasStrikethrough = false;
bool emptyMarkers = false;
QString text = cursor.block().text();
if (pos <= text.size() - 2) {
if (text[pos] == '~' && text[pos + 1] == '~') {
hasStrikethrough = true;
if (pos >= 2
&& text[pos - 1] == '~'
&& text[pos - 2] == '~') {
emptyMarkers = true;
}
}
}
if (hasStrikethrough) {
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
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);
}
} else {
cursor.insertText("~~~~");
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);

View File

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