mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support Insert Code Block tool bar button
Ctrl+M to insert a code block.
This commit is contained in:
parent
e66b70b6ff
commit
30dfc24a28
@ -49,13 +49,17 @@ Save current changes and exit edit mode.
|
|||||||
|
|
||||||
#### Text Editing
|
#### Text Editing
|
||||||
- `Ctrl+B`
|
- `Ctrl+B`
|
||||||
Insert bold. Press `Ctrl+B` again to exit. Current selected text will be changed to bold if exist.
|
Insert bold. Press `Ctrl+B` again to exit. Current selected text will be changed to bold if exists.
|
||||||
- `Ctrl+I`
|
- `Ctrl+I`
|
||||||
Insert italic. Press `Ctrl+I` again to exit. Current selected text will be changed to italic if exist.
|
Insert italic. Press `Ctrl+I` again to exit. Current selected text will be changed to italic if exists.
|
||||||
- `Ctrl+D`
|
- `Ctrl+D`
|
||||||
Insert strikethrought. Press `Ctrl+D` again to exit. Current selected text will be changed to strikethrough if exist.
|
Insert strikethrought. Press `Ctrl+D` again to exit. Current selected text will be changed to strikethrough if exists.
|
||||||
- `Ctrl+O`
|
- `Ctrl+O`
|
||||||
Insert inline code. Press `Ctrl+O` again to exit. Current selected text will be changed to inline code if exist.
|
Insert inline code. Press `Ctrl+O` again to exit. Current selected text will be changed to inline code if exists.
|
||||||
|
- `Ctrl+M`
|
||||||
|
Insert fenced code block. Press `Ctrl+M` again to exit. Current selected text will be wrapped into a code block if exists.
|
||||||
|
- `Ctrl+L`
|
||||||
|
Insert link.
|
||||||
- `Ctrl+H`
|
- `Ctrl+H`
|
||||||
Backspace. Delete a character backward.
|
Backspace. Delete a character backward.
|
||||||
- `Ctrl+W`
|
- `Ctrl+W`
|
||||||
@ -63,7 +67,7 @@ Delete all the characters from current cursor to the first space backward.
|
|||||||
- `Ctrl+U`
|
- `Ctrl+U`
|
||||||
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 exist.
|
Insert title at level `<Num>`. `<Num>` should be 1 to 6. Current selected text will be changed to title if exists.
|
||||||
- `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`
|
||||||
|
@ -56,6 +56,10 @@
|
|||||||
插入删除线;再次按`Ctrl+D`退出。如果已经选择文本,则将当前选择文本改为删除线。
|
插入删除线;再次按`Ctrl+D`退出。如果已经选择文本,则将当前选择文本改为删除线。
|
||||||
- `Ctrl+O`
|
- `Ctrl+O`
|
||||||
插入行内代码;再次按`Ctrl+O`退出。如果已经选择文本,则将当前选择文本改为行内代码。
|
插入行内代码;再次按`Ctrl+O`退出。如果已经选择文本,则将当前选择文本改为行内代码。
|
||||||
|
- `Ctrl+M`
|
||||||
|
插入代码块;再次按`Ctrl+M`退出。如果已经选择文本,则将当前选择文本嵌入到代码块中。
|
||||||
|
- `Ctrl+L`
|
||||||
|
插入链接。
|
||||||
- `Ctrl+H`
|
- `Ctrl+H`
|
||||||
退格键,向前删除一个字符。
|
退格键,向前删除一个字符。
|
||||||
- `Ctrl+W`
|
- `Ctrl+W`
|
||||||
|
6
src/resources/icons/code_block.svg
Normal file
6
src/resources/icons/code_block.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<g>
|
||||||
|
<title>Layer 1</title>
|
||||||
|
<text style="cursor: move;" fill="#000000" stroke-width="0" x="-146.75684" y="248.49038" id="svg_4" font-size="24" font-family="serif" text-anchor="middle" xml:space="preserve" font-weight="bold" transform="matrix(16.72881317138672,0,0,16.72881317138672,2707.567729830742,-3759.186347961426) " stroke="#000000">#</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 470 B |
@ -48,7 +48,7 @@ bool VEditUtils::insertBlockWithIndent(QTextCursor &p_cursor)
|
|||||||
{
|
{
|
||||||
V_ASSERT(!p_cursor.hasSelection());
|
V_ASSERT(!p_cursor.hasSelection());
|
||||||
p_cursor.insertBlock();
|
p_cursor.insertBlock();
|
||||||
return indentBlockAsPreviousBlock(p_cursor);
|
return indentBlockAsBlock(p_cursor, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor)
|
bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor)
|
||||||
@ -85,21 +85,16 @@ bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEditUtils::indentBlockAsPreviousBlock(QTextCursor &p_cursor)
|
bool VEditUtils::indentBlockAsBlock(QTextCursor &p_cursor, bool p_next)
|
||||||
{
|
{
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
QTextBlock block = p_cursor.block();
|
QTextBlock block = p_cursor.block();
|
||||||
if (block.blockNumber() == 0) {
|
QTextBlock refBlock = p_next ? block.next() : block.previous();
|
||||||
// The first block.
|
if (!refBlock.isValid()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextBlock preBlock = block.previous();
|
QString leadingSpaces = fetchIndentSpaces(refBlock);
|
||||||
QString text = preBlock.text();
|
|
||||||
QRegExp regExp("(^\\s*)");
|
|
||||||
regExp.indexIn(text);
|
|
||||||
V_ASSERT(regExp.captureCount() == 1);
|
|
||||||
QString leadingSpaces = regExp.capturedTexts()[1];
|
|
||||||
|
|
||||||
moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::MoveAnchor);
|
moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::MoveAnchor);
|
||||||
if (!p_cursor.atBlockStart()) {
|
if (!p_cursor.atBlockStart()) {
|
||||||
@ -789,3 +784,30 @@ void VEditUtils::findCurrentWORD(const QTextCursor &p_cursor,
|
|||||||
p_end += block.position();
|
p_end += block.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VEditUtils::fetchIndentSpaces(const QTextBlock &p_block)
|
||||||
|
{
|
||||||
|
QString text = p_block.text();
|
||||||
|
QRegExp regExp("(^\\s*)");
|
||||||
|
regExp.indexIn(text);
|
||||||
|
Q_ASSERT(regExp.captureCount() == 1);
|
||||||
|
return regExp.capturedTexts()[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditUtils::insertBlock(QTextCursor &p_cursor,
|
||||||
|
bool p_above)
|
||||||
|
{
|
||||||
|
p_cursor.movePosition(p_above ? QTextCursor::StartOfBlock
|
||||||
|
: QTextCursor::EndOfBlock,
|
||||||
|
QTextCursor::MoveAnchor,
|
||||||
|
1);
|
||||||
|
|
||||||
|
p_cursor.insertBlock();
|
||||||
|
|
||||||
|
if (p_above) {
|
||||||
|
p_cursor.movePosition(QTextCursor::PreviousBlock,
|
||||||
|
QTextCursor::MoveAnchor,
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
p_cursor.movePosition(QTextCursor::EndOfBlock);
|
||||||
|
}
|
||||||
|
@ -24,10 +24,11 @@ public:
|
|||||||
// Need to call setTextCursor() to make it take effect.
|
// Need to call setTextCursor() to make it take effect.
|
||||||
static void moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor,
|
static void moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor,
|
||||||
QTextCursor::MoveMode p_mode);
|
QTextCursor::MoveMode p_mode);
|
||||||
// Indent current block as previous block.
|
// Indent current block as next/previous block.
|
||||||
// Return true if some changes have been made.
|
// Return true if some changes have been made.
|
||||||
// @p_cursor will be placed at the position after inserting leading spaces.
|
// @p_cursor will be placed at the position after inserting leading spaces.
|
||||||
static bool indentBlockAsPreviousBlock(QTextCursor &p_cursor);
|
// @p_next: indent as next block or previous block.
|
||||||
|
static bool indentBlockAsBlock(QTextCursor &p_cursor, bool p_next);
|
||||||
|
|
||||||
// Returns true if two blocks has the same indent.
|
// Returns true if two blocks has the same indent.
|
||||||
static bool hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb);
|
static bool hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb);
|
||||||
@ -157,6 +158,14 @@ public:
|
|||||||
int &p_start,
|
int &p_start,
|
||||||
int &p_end);
|
int &p_end);
|
||||||
|
|
||||||
|
// Return the leading spaces of @p_block.
|
||||||
|
static QString fetchIndentSpaces(const QTextBlock &p_block);
|
||||||
|
|
||||||
|
// Insert a block above/below current block. Move the cursor to the start of
|
||||||
|
// the new block after insertion.
|
||||||
|
static void insertBlock(QTextCursor &p_cursor,
|
||||||
|
bool p_above);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VEditUtils() {}
|
VEditUtils() {}
|
||||||
};
|
};
|
||||||
|
@ -296,7 +296,7 @@ static void insertChangeBlockAfterDeletion(QTextCursor &p_cursor, int p_deletion
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (g_config->getAutoIndent()) {
|
if (g_config->getAutoIndent()) {
|
||||||
VEditUtils::indentBlockAsPreviousBlock(p_cursor);
|
VEditUtils::indentBlockAsBlock(p_cursor, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -911,7 +911,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
|
|
||||||
bool textInserted = false;
|
bool textInserted = false;
|
||||||
if (g_config->getAutoIndent()) {
|
if (g_config->getAutoIndent()) {
|
||||||
textInserted = VEditUtils::indentBlockAsPreviousBlock(cursor);
|
textInserted = VEditUtils::indentBlockAsBlock(cursor, false);
|
||||||
if (g_config->getAutoList()) {
|
if (g_config->getAutoList()) {
|
||||||
textInserted = VEditUtils::insertListMarkAsPreviousBlock(cursor)
|
textInserted = VEditUtils::insertListMarkAsPreviousBlock(cursor)
|
||||||
|| textInserted;
|
|| textInserted;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
class VNavigationMode;
|
class VNavigationMode;
|
||||||
|
class QShortcut;
|
||||||
|
|
||||||
// void func(void *p_target, void *p_data);
|
// void func(void *p_target, void *p_data);
|
||||||
typedef std::function<void(void *, void *)> CaptainFunc;
|
typedef std::function<void(void *, void *)> CaptainFunc;
|
||||||
|
@ -52,12 +52,16 @@ namespace DirConfig
|
|||||||
|
|
||||||
static const QString c_emptyHeaderName = "[EMPTY]";
|
static const QString c_emptyHeaderName = "[EMPTY]";
|
||||||
|
|
||||||
enum class TextDecoration { None,
|
enum class TextDecoration
|
||||||
Bold,
|
{
|
||||||
Italic,
|
None,
|
||||||
Underline,
|
Bold,
|
||||||
Strikethrough,
|
Italic,
|
||||||
InlineCode };
|
Underline,
|
||||||
|
Strikethrough,
|
||||||
|
InlineCode,
|
||||||
|
CodeBlock
|
||||||
|
};
|
||||||
|
|
||||||
enum FindOption
|
enum FindOption
|
||||||
{
|
{
|
||||||
|
@ -463,6 +463,21 @@ void VMainWindow::initEditToolBar(QSize p_iconSize)
|
|||||||
|
|
||||||
m_editToolBar->addAction(inlineCodeAct);
|
m_editToolBar->addAction(inlineCodeAct);
|
||||||
|
|
||||||
|
QAction *codeBlockAct = new QAction(QIcon(":/resources/icons/code_block.svg"),
|
||||||
|
tr("Code Block (Ctrl+M)"),
|
||||||
|
this);
|
||||||
|
codeBlockAct->setStatusTip(tr("Insert fenced code block text or wrap selected text into a fenced code block"));
|
||||||
|
connect(codeBlockAct, &QAction::triggered,
|
||||||
|
this, [this](){
|
||||||
|
if (m_curTab) {
|
||||||
|
m_curTab->decorateText(TextDecoration::CodeBlock);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_editToolBar->addAction(codeBlockAct);
|
||||||
|
|
||||||
|
m_editToolBar->addSeparator();
|
||||||
|
|
||||||
// Insert link.
|
// Insert link.
|
||||||
QAction *insetLinkAct = new QAction(QIcon(":/resources/icons/link.svg"),
|
QAction *insetLinkAct = new QAction(QIcon(":/resources/icons/link.svg"),
|
||||||
tr("Insert Link (Ctrl+L)"), this);
|
tr("Insert Link (Ctrl+L)"), this);
|
||||||
|
@ -289,6 +289,17 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Qt::Key_M:
|
||||||
|
{
|
||||||
|
if (modifiers == Qt::ControlModifier) {
|
||||||
|
decorateCodeBlock();
|
||||||
|
p_event->accept();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_O:
|
case Qt::Key_O:
|
||||||
{
|
{
|
||||||
if (modifiers == Qt::ControlModifier) {
|
if (modifiers == Qt::ControlModifier) {
|
||||||
@ -684,6 +695,10 @@ void VMdEditOperations::decorateText(TextDecoration p_decoration)
|
|||||||
decorateInlineCode();
|
decorateInlineCode();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TextDecoration::CodeBlock:
|
||||||
|
decorateCodeBlock();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
validDecoration = false;
|
validDecoration = false;
|
||||||
qDebug() << "decoration" << (int)p_decoration << "is not implemented yet";
|
qDebug() << "decoration" << (int)p_decoration << "is not implemented yet";
|
||||||
@ -807,6 +822,86 @@ void VMdEditOperations::decorateInlineCode()
|
|||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMdEditOperations::decorateCodeBlock()
|
||||||
|
{
|
||||||
|
const QString marker("```");
|
||||||
|
|
||||||
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
|
cursor.beginEditBlock();
|
||||||
|
if (cursor.hasSelection()) {
|
||||||
|
// Insert ``` around the selected text.
|
||||||
|
int start = cursor.selectionStart();
|
||||||
|
int end = cursor.selectionEnd();
|
||||||
|
|
||||||
|
QString indentation = VEditUtils::fetchIndentSpaces(cursor.block());
|
||||||
|
|
||||||
|
// Insert the end marker first.
|
||||||
|
cursor.setPosition(end, QTextCursor::MoveAnchor);
|
||||||
|
VEditUtils::insertBlock(cursor, false);
|
||||||
|
VEditUtils::indentBlock(cursor, indentation);
|
||||||
|
cursor.insertText(marker);
|
||||||
|
|
||||||
|
// Insert the start marker.
|
||||||
|
cursor.setPosition(start, QTextCursor::MoveAnchor);
|
||||||
|
VEditUtils::insertBlock(cursor, true);
|
||||||
|
VEditUtils::indentBlock(cursor, indentation);
|
||||||
|
cursor.insertText(marker);
|
||||||
|
} else {
|
||||||
|
// Insert ``` ``` and place cursor after the first marker.
|
||||||
|
// Or if current block or next block is ```, we will skip it.
|
||||||
|
QTextBlock block = cursor.block();
|
||||||
|
int state = block.userState();
|
||||||
|
if (state == HighlightBlockState::CodeBlock
|
||||||
|
|| state == HighlightBlockState::CodeBlockStart
|
||||||
|
|| state == HighlightBlockState::CodeBlockEnd) {
|
||||||
|
// Find the block end.
|
||||||
|
while (block.isValid()) {
|
||||||
|
if (block.userState() == HighlightBlockState::CodeBlockEnd) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
block = block.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.isValid()) {
|
||||||
|
// It is CodeBlockEnd.
|
||||||
|
cursor.setPosition(block.position());
|
||||||
|
if (block.next().isValid()) {
|
||||||
|
cursor.movePosition(QTextCursor::NextBlock);
|
||||||
|
cursor.movePosition(QTextCursor::StartOfBlock);
|
||||||
|
} else {
|
||||||
|
cursor.movePosition(QTextCursor::EndOfBlock);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reach the end of the document.
|
||||||
|
cursor.movePosition(QTextCursor::End);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool insertInline = false;
|
||||||
|
if (!cursor.atBlockEnd()) {
|
||||||
|
cursor.insertBlock();
|
||||||
|
cursor.movePosition(QTextCursor::PreviousBlock);
|
||||||
|
} else if (cursor.atBlockStart()) {
|
||||||
|
insertInline = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!insertInline) {
|
||||||
|
VEditUtils::insertBlock(cursor, false);
|
||||||
|
VEditUtils::indentBlockAsBlock(cursor, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.insertText(marker);
|
||||||
|
|
||||||
|
VEditUtils::insertBlock(cursor, true);
|
||||||
|
VEditUtils::indentBlockAsBlock(cursor, true);
|
||||||
|
cursor.insertText(marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.endEditBlock();
|
||||||
|
m_editor->setTextCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
void VMdEditOperations::decorateStrikethrough()
|
void VMdEditOperations::decorateStrikethrough()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
|
@ -72,6 +72,9 @@ private:
|
|||||||
// Insert inline-code marker or set selected text inline-coded.
|
// Insert inline-code marker or set selected text inline-coded.
|
||||||
void decorateInlineCode();
|
void decorateInlineCode();
|
||||||
|
|
||||||
|
// Insert inline-code marker or set selected text inline-coded.
|
||||||
|
void decorateCodeBlock();
|
||||||
|
|
||||||
// Insert strikethrough marker or set selected text strikethrough.
|
// Insert strikethrough marker or set selected text strikethrough.
|
||||||
void decorateStrikethrough();
|
void decorateStrikethrough();
|
||||||
|
|
||||||
|
@ -133,5 +133,6 @@
|
|||||||
<file>resources/icons/compact_mode.svg</file>
|
<file>resources/icons/compact_mode.svg</file>
|
||||||
<file>resources/icons/heading_sequence.svg</file>
|
<file>resources/icons/heading_sequence.svg</file>
|
||||||
<file>resources/icons/link.svg</file>
|
<file>resources/icons/link.svg</file>
|
||||||
|
<file>resources/icons/code_block.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user