feat: v0.7.0 —— 代码块带边框和复制按钮,代码文字不再发红

- 围栏代码块改成等宽 + 普通文字色(之前整块都是代码色,看起来像报错)
- 编辑模式下给每个代码块画圆角边框,右上角一个「复制」按钮,
  点击把代码内容复制到剪贴板,并把按钮临时改成「已复制」
- 复制按钮跟随滚动 / 缩放重算位置,hover 时变高亮
- 源代码模式不画框也不显示按钮,保持纯源码观感
- 复制 / 已复制 已加入中英文翻译
- 截图与 README 同步,版本 0.7.0
This commit is contained in:
2026-10-22 18:10:02 +08:00
parent 5d1fb0fab7
commit a136ed1eaa
11 changed files with 347 additions and 130 deletions
+3 -1
View File
@@ -2,7 +2,7 @@
一个轻量的跨平台 Markdown 编辑器,基于 **Qt 5 / C++17** 构建。 一个轻量的跨平台 Markdown 编辑器,基于 **Qt 5 / C++17** 构建。
当前版本 **0.6.0**。 当前版本 **0.7.0**。
## 界面 ## 界面
@@ -24,6 +24,8 @@
- `Ctrl+/` 把选中内容包成围栏代码块,再按一次拆掉 - `Ctrl+/` 把选中内容包成围栏代码块,再按一次拆掉
- 默认「编辑模式」隐藏 Markdown 标记、标题按级别放大,光标所在行露出原始标记 - 默认「编辑模式」隐藏 Markdown 标记、标题按级别放大,光标所在行露出原始标记
- 行号栏、当前行高亮 - 行号栏、当前行高亮
- 围栏代码块显示成带圆角边框的面板,右上角有「复制」按钮
(源代码模式下不显示这些装饰)
- `Tab` / `Shift+Tab` 整行缩进,选中多行时批量缩进 - `Tab` / `Shift+Tab` 整行缩进,选中多行时批量缩进
- 自动换行(`Alt+Z`)、`Ctrl+滚轮` 或 `Ctrl++` / `Ctrl+-` 缩放字号 - 自动换行(`Alt+Z`)、`Ctrl+滚轮` 或 `Ctrl++` / `Ctrl+-` 缩放字号
- 撤销 / 重做、剪切 / 复制 / 粘贴,菜单项会跟随可用状态自动灰显 - 撤销 / 重做、剪切 / 复制 / 粘贴,菜单项会跟随可用状态自动灰显
+1 -1
View File
@@ -1,6 +1,6 @@
TEMPLATE = app TEMPLATE = app
TARGET = VoiletMark TARGET = VoiletMark
VERSION = 0.6.0 VERSION = 0.7.0
QT += core gui widgets printsupport QT += core gui widgets printsupport
Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 115 KiB

+183 -17
View File
@@ -4,12 +4,15 @@
#include "ThemeManager.h" #include "ThemeManager.h"
#include <QAbstractTextDocumentLayout> #include <QAbstractTextDocumentLayout>
#include <QClipboard>
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QDropEvent> #include <QDropEvent>
#include <QFileInfo> #include <QFileInfo>
#include <QFontDatabase> #include <QFontDatabase>
#include <QFontMetricsF> #include <QFontMetricsF>
#include <QGuiApplication>
#include <QKeyEvent> #include <QKeyEvent>
#include <QMouseEvent>
#include <QMimeData> #include <QMimeData>
#include <QPainter> #include <QPainter>
#include <QPaintEvent> #include <QPaintEvent>
@@ -17,6 +20,7 @@
#include <QTextBlock> #include <QTextBlock>
#include <QTextCursor> #include <QTextCursor>
#include <QTextOption> #include <QTextOption>
#include <QTimer>
#include <QUrl> #include <QUrl>
#include <QWheelEvent> #include <QWheelEvent>
@@ -88,6 +92,7 @@ MarkdownEditor::MarkdownEditor(QWidget *parent)
setLineWrapMode(QPlainTextEdit::WidgetWidth); setLineWrapMode(QPlainTextEdit::WidgetWidth);
setCursorWidth(2); setCursorWidth(2);
setAcceptDrops(true); setAcceptDrops(true);
viewport()->setMouseTracking(true);
document()->setDocumentMargin(18); document()->setDocumentMargin(18);
m_lineNumberArea = new LineNumberArea(this); m_lineNumberArea = new LineNumberArea(this);
@@ -106,9 +111,16 @@ MarkdownEditor::MarkdownEditor(QWidget *parent)
connect(ThemeManager::instance(), &ThemeManager::schemeChanged, connect(ThemeManager::instance(), &ThemeManager::schemeChanged,
this, &MarkdownEditor::applyTheme); this, &MarkdownEditor::applyTheme);
m_fenceTimer = new QTimer(this);
m_fenceTimer->setSingleShot(true);
m_fenceTimer->setInterval(120);
connect(m_fenceTimer, &QTimer::timeout, this, &MarkdownEditor::rebuildCodeFences);
connect(this, &QPlainTextEdit::textChanged, this, [this] { m_fenceTimer->start(); });
m_lineNumberArea->setVisible(m_sourceMode); m_lineNumberArea->setVisible(m_sourceMode);
applyEditorFont(); applyEditorFont();
updateCurrentLineHighlight(); updateCurrentLineHighlight();
rebuildCodeFences();
} }
int MarkdownEditor::lineNumberAreaWidth() const int MarkdownEditor::lineNumberAreaWidth() const
@@ -483,27 +495,181 @@ void MarkdownEditor::resizeEvent(QResizeEvent *event)
void MarkdownEditor::paintEvent(QPaintEvent *event) void MarkdownEditor::paintEvent(QPaintEvent *event)
{ {
QPlainTextEdit::paintEvent(event); QPlainTextEdit::paintEvent(event);
if (!m_sourceMode)
QPainter painter(viewport());
if (m_sourceMode) {
// 源代码模式:每行底下画一条极细的分隔线,像稿纸一样。
QColor lineColor = ThemeManager::instance()->colors().border;
lineColor.setAlpha(120);
painter.setPen(lineColor);
QTextBlock block = firstVisibleBlock();
int top = qRound(blockBoundingGeometry(block).translated(contentOffset()).top());
int bottom = top + qRound(blockBoundingRect(block).height());
const int width = viewport()->width();
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top())
painter.drawLine(0, bottom - 1, width, bottom - 1);
block = block.next();
top = bottom;
bottom = top + qRound(blockBoundingRect(block).height());
}
return;
}
// 编辑模式:代码块套一个圆角框,右上角给个复制按钮。
const ThemeColors &colors = ThemeManager::instance()->colors();
const QRectF visible(event->rect());
for (int index = 0; index < m_codeFences.size(); ++index) {
const QRectF rect = codeFenceRect(index);
if (!rect.isValid() || !rect.intersects(visible))
continue;
painter.setBrush(Qt::NoBrush);
painter.setPen(QPen(colors.border, 1));
painter.drawRoundedRect(rect.adjusted(0.5, 0.5, -0.5, -0.5), 6, 6);
const QRectF button = copyButtonRect(index);
if (!button.isValid())
continue;
QFont small = font();
small.setPointSizeF(qMax(7.0, font().pointSizeF() - 2.0));
painter.setFont(small);
const bool hovered = (m_hoveredFence == index);
const bool copied = (m_copiedFence == index);
painter.setPen(QPen(hovered ? colors.accent : colors.border, 1));
painter.setBrush(colors.editorBase);
painter.drawRoundedRect(button, 4, 4);
painter.setPen(copied ? colors.accent : colors.placeholder);
painter.drawText(button, Qt::AlignCenter, copied ? tr("已复制") : tr("复制"));
}
}
QRectF MarkdownEditor::codeFenceRect(int index) const
{
if (index < 0 || index >= m_codeFences.size())
return QRectF();
const CodeFence &fence = m_codeFences.at(index);
const QTextBlock first = document()->findBlockByNumber(fence.open);
const int lastNumber = fence.close >= 0 ? fence.close : document()->blockCount() - 1;
const QTextBlock last = document()->findBlockByNumber(lastNumber);
if (!first.isValid() || !last.isValid())
return QRectF();
const QRectF firstRect = blockBoundingGeometry(first).translated(contentOffset());
const QRectF lastRect = blockBoundingGeometry(last).translated(contentOffset());
const qreal top = firstRect.top() - 3;
const qreal bottom = lastRect.bottom() + 3;
return QRectF(3, top, viewport()->width() - 6, bottom - top);
}
QRectF MarkdownEditor::copyButtonRect(int index) const
{
const QRectF rect = codeFenceRect(index);
if (!rect.isValid())
return QRectF();
QFont small = font();
small.setPointSizeF(qMax(7.0, font().pointSizeF() - 2.0));
const QFontMetricsF metrics(small);
const QString label = (m_copiedFence == index) ? tr("已复制") : tr("复制");
const qreal width = metrics.horizontalAdvance(label) + 16;
const qreal height = metrics.height() + 6;
return QRectF(rect.right() - width - 8, rect.top() + 6, width, height);
}
QString MarkdownEditor::codeFenceText(int index) const
{
if (index < 0 || index >= m_codeFences.size())
return QString();
const CodeFence &fence = m_codeFences.at(index);
const int end = fence.close >= 0 ? fence.close : document()->blockCount();
QStringList lines;
for (int number = fence.open + 1; number < end; ++number)
lines << document()->findBlockByNumber(number).text();
return lines.join(QLatin1Char('\n'));
}
void MarkdownEditor::rebuildCodeFences()
{
m_codeFences.clear();
if (!document())
return; return;
// 源代码模式:每行底下画一条极细的分隔线,像稿纸一样。 static const QRegularExpression fencePattern(QStringLiteral("^\\s{0,3}(?:```|~~~)"));
QPainter painter(viewport()); bool inFence = false;
QColor lineColor = ThemeManager::instance()->colors().border; int open = -1;
lineColor.setAlpha(120);
painter.setPen(lineColor);
QTextBlock block = firstVisibleBlock(); for (QTextBlock block = document()->firstBlock(); block.isValid(); block = block.next()) {
int top = qRound(blockBoundingGeometry(block).translated(contentOffset()).top()); if (!fencePattern.match(block.text()).hasMatch())
int bottom = top + qRound(blockBoundingRect(block).height()); continue;
const int width = viewport()->width(); if (!inFence) {
inFence = true;
while (block.isValid() && top <= event->rect().bottom()) { open = block.blockNumber();
if (block.isVisible() && bottom >= event->rect().top()) } else {
painter.drawLine(0, bottom - 1, width, bottom - 1); CodeFence fence;
block = block.next(); fence.open = open;
top = bottom; fence.close = block.blockNumber();
bottom = top + qRound(blockBoundingRect(block).height()); m_codeFences.append(fence);
inFence = false;
open = -1;
}
} }
if (inFence && open >= 0) {
CodeFence fence;
fence.open = open;
m_codeFences.append(fence);
}
viewport()->update();
}
void MarkdownEditor::mouseMoveEvent(QMouseEvent *event)
{
QPlainTextEdit::mouseMoveEvent(event);
if (m_sourceMode)
return;
int hovered = -1;
for (int index = 0; index < m_codeFences.size(); ++index) {
if (copyButtonRect(index).contains(event->pos())) {
hovered = index;
break;
}
}
if (hovered != m_hoveredFence) {
m_hoveredFence = hovered;
viewport()->setCursor(hovered >= 0 ? Qt::PointingHandCursor : Qt::IBeamCursor);
viewport()->update();
}
}
void MarkdownEditor::mousePressEvent(QMouseEvent *event)
{
if (!m_sourceMode && event->button() == Qt::LeftButton) {
for (int index = 0; index < m_codeFences.size(); ++index) {
if (!copyButtonRect(index).contains(event->pos()))
continue;
QGuiApplication::clipboard()->setText(codeFenceText(index));
m_copiedFence = index;
QTimer::singleShot(1200, this, [this] {
m_copiedFence = -1;
viewport()->update();
});
viewport()->update();
return;
}
}
QPlainTextEdit::mousePressEvent(event);
} }
void MarkdownEditor::keyPressEvent(QKeyEvent *event) void MarkdownEditor::keyPressEvent(QKeyEvent *event)
+18
View File
@@ -1,9 +1,11 @@
#pragma once #pragma once
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QVector>
class MarkdownHighlighter; class MarkdownHighlighter;
class QTextBlock; class QTextBlock;
class QTimer;
/// 编辑器本体:等宽字体、行号栏、当前行高亮、Tab 缩进、 /// 编辑器本体:等宽字体、行号栏、当前行高亮、Tab 缩进、
/// Ctrl+滚轮缩放,以及把拖入的文件转交给主窗口打开。 /// Ctrl+滚轮缩放,以及把拖入的文件转交给主窗口打开。
@@ -42,6 +44,8 @@ signals:
protected: protected:
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void keyPressEvent(QKeyEvent *event) override; void keyPressEvent(QKeyEvent *event) override;
void wheelEvent(QWheelEvent *event) override; void wheelEvent(QWheelEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override;
@@ -54,20 +58,34 @@ private slots:
void updateCurrentLineHighlight(); void updateCurrentLineHighlight();
void updateActiveBlock(); void updateActiveBlock();
void applyTheme(); void applyTheme();
void rebuildCodeFences();
private: private:
void applyEditorFont(); void applyEditorFont();
void updateViewportMargins(); void updateViewportMargins();
void indentSelection(bool outdent); void indentSelection(bool outdent);
QRectF codeFenceRect(int index) const;
QRectF copyButtonRect(int index) const;
QString codeFenceText(int index) const;
bool acceptsLocalFiles(const QMimeData *mime) const; bool acceptsLocalFiles(const QMimeData *mime) const;
void selectedBlockRange(int *firstBlock, int *lastBlock) const; void selectedBlockRange(int *firstBlock, int *lastBlock) const;
void selectBlocks(int firstBlock, int lastBlock); void selectBlocks(int firstBlock, int lastBlock);
bool enclosingFence(int firstBlock, int lastBlock, bool enclosingFence(int firstBlock, int lastBlock,
QTextBlock *opener, QTextBlock *closer) const; QTextBlock *opener, QTextBlock *closer) const;
struct CodeFence {
int open = -1; // 开围栏所在行
int close = -1; // 闭围栏所在行,未闭合时为 -1
};
QWidget *m_lineNumberArea = nullptr; QWidget *m_lineNumberArea = nullptr;
MarkdownHighlighter *m_highlighter = nullptr; MarkdownHighlighter *m_highlighter = nullptr;
int m_fontPointSize = 12; int m_fontPointSize = 12;
bool m_sourceMode = false; bool m_sourceMode = false;
int m_activeBlock = -1; int m_activeBlock = -1;
QVector<CodeFence> m_codeFences;
QTimer *m_fenceTimer = nullptr;
int m_hoveredFence = -1;
int m_copiedFence = -1;
}; };
+20 -5
View File
@@ -97,6 +97,12 @@ void MarkdownHighlighter::rebuildFormats()
m_codeFormat.setFontFamilies(monospaceFamilies()); m_codeFormat.setFontFamilies(monospaceFamilies());
m_codeFormat.setFontFixedPitch(true); m_codeFormat.setFontFixedPitch(true);
// 围栏代码块:等宽、普通文字色,不带底色——外面由编辑器画框。
m_fenceFormat = QTextCharFormat();
m_fenceFormat.setFontFamilies(monospaceFamilies());
m_fenceFormat.setFontFixedPitch(true);
m_fenceFormat.setForeground(c.editorText);
m_linkFormat = QTextCharFormat(); m_linkFormat = QTextCharFormat();
m_linkFormat.setForeground(c.link); m_linkFormat.setForeground(c.link);
m_linkFormat.setFontUnderline(true); m_linkFormat.setFontUnderline(true);
@@ -198,8 +204,13 @@ void MarkdownHighlighter::highlightBlock(const QString &text)
if (inFence) { if (inFence) {
if (!fenceLine) if (!fenceLine)
setCurrentBlockState(StateInFence); setCurrentBlockState(StateInFence);
if (rendered && fenceLine && !active) { if (rendered) {
apply(0, text.length(), hiddenFormat(c.editorBase)); if (fenceLine && !active) {
apply(0, text.length(), hiddenFormat(c.editorBase));
} else {
m_blockBase = m_fenceFormat;
apply(0, text.length(), m_fenceFormat);
}
} else { } else {
m_blockBase = m_codeFormat; m_blockBase = m_codeFormat;
apply(0, text.length(), m_codeFormat); apply(0, text.length(), m_codeFormat);
@@ -208,10 +219,14 @@ void MarkdownHighlighter::highlightBlock(const QString &text)
} }
if (fenceLine) { if (fenceLine) {
setCurrentBlockState(StateInFence); setCurrentBlockState(StateInFence);
if (rendered && !active) if (rendered) {
apply(0, text.length(), hiddenFormat(c.editorBase)); if (!active)
else apply(0, text.length(), hiddenFormat(c.editorBase));
else
apply(0, text.length(), m_fenceFormat);
} else {
apply(0, text.length(), m_codeFormat); apply(0, text.length(), m_codeFormat);
}
return; return;
} }
+1
View File
@@ -55,6 +55,7 @@ private:
QTextCharFormat m_boldItalicFormat; QTextCharFormat m_boldItalicFormat;
QTextCharFormat m_emphasisFormat; QTextCharFormat m_emphasisFormat;
QTextCharFormat m_codeFormat; QTextCharFormat m_codeFormat;
QTextCharFormat m_fenceFormat;
QTextCharFormat m_linkFormat; QTextCharFormat m_linkFormat;
QTextCharFormat m_urlFormat; QTextCharFormat m_urlFormat;
QTextCharFormat m_quoteFormat; QTextCharFormat m_quoteFormat;
Binary file not shown.
+121 -106
View File
@@ -3,372 +3,387 @@
<context> <context>
<name>MainWindow</name> <name>MainWindow</name>
<message> <message>
<location filename="../src/MainWindow.cpp" line="167" /> <location filename="../src/MainWindow.cpp" line="170" />
<location filename="../src/MainWindow.cpp" line="781" /> <location filename="../src/MainWindow.cpp" line="784" />
<source>新建</source> <source>新建</source>
<translation>New</translation> <translation>New</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="171" /> <location filename="../src/MainWindow.cpp" line="174" />
<location filename="../src/MainWindow.cpp" line="782" /> <location filename="../src/MainWindow.cpp" line="785" />
<source>打开…</source> <source>打开…</source>
<translation>Open…</translation> <translation>Open…</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="175" /> <location filename="../src/MainWindow.cpp" line="178" />
<location filename="../src/MainWindow.cpp" line="784" /> <location filename="../src/MainWindow.cpp" line="787" />
<source>保存</source> <source>保存</source>
<translation>Save</translation> <translation>Save</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="179" /> <location filename="../src/MainWindow.cpp" line="182" />
<location filename="../src/MainWindow.cpp" line="785" /> <location filename="../src/MainWindow.cpp" line="788" />
<source>另存为…</source> <source>另存为…</source>
<translation>Save As…</translation> <translation>Save As…</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="183" /> <location filename="../src/MainWindow.cpp" line="186" />
<location filename="../src/MainWindow.cpp" line="786" /> <location filename="../src/MainWindow.cpp" line="789" />
<source>导出 PDF…</source> <source>导出 PDF…</source>
<translation>Export PDF…</translation> <translation>Export PDF…</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="187" /> <location filename="../src/MainWindow.cpp" line="190" />
<location filename="../src/MainWindow.cpp" line="787" /> <location filename="../src/MainWindow.cpp" line="790" />
<source>退出</source> <source>退出</source>
<translation>Quit</translation> <translation>Quit</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="191" /> <location filename="../src/MainWindow.cpp" line="194" />
<location filename="../src/MainWindow.cpp" line="789" /> <location filename="../src/MainWindow.cpp" line="792" />
<source>撤销</source> <source>撤销</source>
<translation>Undo</translation> <translation>Undo</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="195" /> <location filename="../src/MainWindow.cpp" line="198" />
<location filename="../src/MainWindow.cpp" line="790" /> <location filename="../src/MainWindow.cpp" line="793" />
<source>重做</source> <source>重做</source>
<translation>Redo</translation> <translation>Redo</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="199" /> <location filename="../src/MainWindow.cpp" line="202" />
<location filename="../src/MainWindow.cpp" line="791" /> <location filename="../src/MainWindow.cpp" line="794" />
<source>剪切</source> <source>剪切</source>
<translation>Cut</translation> <translation>Cut</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="203" /> <location filename="../src/MainWindow.cpp" line="206" />
<location filename="../src/MainWindow.cpp" line="792" /> <location filename="../src/MainWindow.cpp" line="795" />
<source>复制</source> <source>复制</source>
<translation>Copy</translation> <translation>Copy</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="207" /> <location filename="../src/MainWindow.cpp" line="210" />
<location filename="../src/MainWindow.cpp" line="793" /> <location filename="../src/MainWindow.cpp" line="796" />
<source>粘贴</source> <source>粘贴</source>
<translation>Paste</translation> <translation>Paste</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="211" /> <location filename="../src/MainWindow.cpp" line="214" />
<location filename="../src/MainWindow.cpp" line="794" /> <location filename="../src/MainWindow.cpp" line="797" />
<source>全选</source> <source>全选</source>
<translation>Select All</translation> <translation>Select All</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="215" /> <location filename="../src/MainWindow.cpp" line="218" />
<location filename="../src/MainWindow.cpp" line="798" /> <location filename="../src/MainWindow.cpp" line="801" />
<source>自动换行</source> <source>自动换行</source>
<translation>Word Wrap</translation> <translation>Word Wrap</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="220" /> <location filename="../src/MainWindow.cpp" line="223" />
<location filename="../src/MainWindow.cpp" line="799" /> <location filename="../src/MainWindow.cpp" line="802" />
<source>放大</source> <source>放大</source>
<translation>Zoom In</translation> <translation>Zoom In</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="224" /> <location filename="../src/MainWindow.cpp" line="227" />
<location filename="../src/MainWindow.cpp" line="800" /> <location filename="../src/MainWindow.cpp" line="803" />
<source>缩小</source> <source>缩小</source>
<translation>Zoom Out</translation> <translation>Zoom Out</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="228" /> <location filename="../src/MainWindow.cpp" line="231" />
<location filename="../src/MainWindow.cpp" line="801" /> <location filename="../src/MainWindow.cpp" line="804" />
<source>实际大小</source> <source>实际大小</source>
<translation>Actual Size</translation> <translation>Actual Size</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="237" /> <location filename="../src/MainWindow.cpp" line="240" />
<location filename="../src/MainWindow.cpp" line="783" /> <location filename="../src/MainWindow.cpp" line="786" />
<source>打开文件夹…</source> <source>打开文件夹…</source>
<translation>Open Folder…</translation> <translation>Open Folder…</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="241" /> <location filename="../src/MainWindow.cpp" line="244" />
<location filename="../src/MainWindow.cpp" line="407" /> <location filename="../src/MainWindow.cpp" line="410" />
<location filename="../src/MainWindow.cpp" line="796" /> <location filename="../src/MainWindow.cpp" line="799" />
<location filename="../src/MainWindow.cpp" line="817" /> <location filename="../src/MainWindow.cpp" line="820" />
<source>侧栏</source> <source>侧栏</source>
<translation>Sidebar</translation> <translation>Sidebar</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="255" /> <location filename="../src/MainWindow.cpp" line="258" />
<location filename="../src/MainWindow.cpp" line="797" /> <location filename="../src/MainWindow.cpp" line="800" />
<source>源代码模式</source> <source>源代码模式</source>
<translation>Source Mode</translation> <translation>Source Mode</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="260" /> <location filename="../src/MainWindow.cpp" line="263" />
<location filename="../src/MainWindow.cpp" line="813" /> <location filename="../src/MainWindow.cpp" line="816" />
<source>代码块</source> <source>代码块</source>
<translation>Code Block</translation> <translation>Code Block</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="267" /> <location filename="../src/MainWindow.cpp" line="270" />
<location filename="../src/MainWindow.cpp" line="811" /> <location filename="../src/MainWindow.cpp" line="814" />
<source>标题 %1</source> <source>标题 %1</source>
<translation>Heading %1</translation> <translation>Heading %1</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="278" /> <location filename="../src/MainWindow.cpp" line="281" />
<source>文件夹</source> <source>文件夹</source>
<translation>Folder</translation> <translation>Folder</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="289" /> <location filename="../src/MainWindow.cpp" line="292" />
<location filename="../src/MainWindow.cpp" line="961" /> <location filename="../src/MainWindow.cpp" line="964" />
<source>未打开文件夹</source> <source>未打开文件夹</source>
<translation>No folder open</translation> <translation>No folder open</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="315" /> <location filename="../src/MainWindow.cpp" line="318" />
<location filename="../src/MainWindow.cpp" line="771" /> <location filename="../src/MainWindow.cpp" line="774" />
<source>文件</source> <source>文件</source>
<translation>File</translation> <translation>File</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="319" /> <location filename="../src/MainWindow.cpp" line="322" />
<location filename="../src/MainWindow.cpp" line="776" /> <location filename="../src/MainWindow.cpp" line="779" />
<source>最近打开</source> <source>最近打开</source>
<translation>Recent Files</translation> <translation>Recent Files</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="327" /> <location filename="../src/MainWindow.cpp" line="330" />
<location filename="../src/MainWindow.cpp" line="772" /> <location filename="../src/MainWindow.cpp" line="775" />
<source>编辑</source> <source>编辑</source>
<translation>Edit</translation> <translation>Edit</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="337" /> <location filename="../src/MainWindow.cpp" line="340" />
<location filename="../src/MainWindow.cpp" line="773" /> <location filename="../src/MainWindow.cpp" line="776" />
<source>视图</source> <source>视图</source>
<translation>View</translation> <translation>View</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="348" /> <location filename="../src/MainWindow.cpp" line="351" />
<location filename="../src/MainWindow.cpp" line="777" /> <location filename="../src/MainWindow.cpp" line="780" />
<source>主题</source> <source>主题</source>
<translation>Theme</translation> <translation>Theme</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="356" /> <location filename="../src/MainWindow.cpp" line="359" />
<location filename="../src/MainWindow.cpp" line="807" /> <location filename="../src/MainWindow.cpp" line="810" />
<source>6:00–18:00 使用浅色,其余时间使用深色</source> <source>6:00–18:00 使用浅色,其余时间使用深色</source>
<translation>Light from 6:00 to 18:00, dark the rest of the day</translation> <translation>Light from 6:00 to 18:00, dark the rest of the day</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="374" /> <location filename="../src/MainWindow.cpp" line="377" />
<location filename="../src/MainWindow.cpp" line="779" /> <location filename="../src/MainWindow.cpp" line="782" />
<source>语言</source> <source>语言</source>
<translation>Language</translation> <translation>Language</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="391" /> <location filename="../src/MainWindow.cpp" line="394" />
<location filename="../src/MainWindow.cpp" line="774" /> <location filename="../src/MainWindow.cpp" line="777" />
<source>格式</source> <source>格式</source>
<translation>Format</translation> <translation>Format</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="392" /> <location filename="../src/MainWindow.cpp" line="395" />
<location filename="../src/MainWindow.cpp" line="778" /> <location filename="../src/MainWindow.cpp" line="781" />
<source>标题</source> <source>标题</source>
<translation>Heading</translation> <translation>Heading</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="399" /> <location filename="../src/MainWindow.cpp" line="402" />
<location filename="../src/MainWindow.cpp" line="775" /> <location filename="../src/MainWindow.cpp" line="778" />
<source>帮助</source> <source>帮助</source>
<translation>Help</translation> <translation>Help</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="400" /> <location filename="../src/MainWindow.cpp" line="403" />
<location filename="../src/MainWindow.cpp" line="814" /> <location filename="../src/MainWindow.cpp" line="817" />
<location filename="../src/MainWindow.cpp" line="844" /> <location filename="../src/MainWindow.cpp" line="847" />
<source>关于 VoiletMark</source> <source>关于 VoiletMark</source>
<translation>About VoiletMark</translation> <translation>About VoiletMark</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="411" /> <location filename="../src/MainWindow.cpp" line="414" />
<location filename="../src/MainWindow.cpp" line="818" /> <location filename="../src/MainWindow.cpp" line="821" />
<source>显示 / 隐藏文件夹侧栏(Ctrl+B)</source> <source>显示 / 隐藏文件夹侧栏(Ctrl+B)</source>
<translation>Show / hide the folder sidebar (Ctrl+B)</translation> <translation>Show / hide the folder sidebar (Ctrl+B)</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="419" /> <location filename="../src/MainWindow.cpp" line="422" />
<location filename="../src/MainWindow.cpp" line="821" /> <location filename="../src/MainWindow.cpp" line="824" />
<source>源代码模式:关闭语法高亮与行号(Ctrl+Shift+M)</source> <source>源代码模式:关闭语法高亮与行号(Ctrl+Shift+M)</source>
<translation>Source mode: turn off highlighting and line numbers (Ctrl+Shift+M)</translation> <translation>Source mode: turn off highlighting and line numbers (Ctrl+Shift+M)</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="497" /> <location filename="../src/MainWindow.cpp" line="500" />
<location filename="../src/MainWindow.cpp" line="612" /> <location filename="../src/MainWindow.cpp" line="615" />
<source>未命名</source> <source>未命名</source>
<translation>Untitled</translation> <translation>Untitled</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="546" /> <location filename="../src/MainWindow.cpp" line="549" />
<source>打开 Markdown 文件</source> <source>打开 Markdown 文件</source>
<translation>Open Markdown File</translation> <translation>Open Markdown File</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="547" /> <location filename="../src/MainWindow.cpp" line="550" />
<source>Markdown 文件 (*.md *.markdown *.mdown *.mkd);;文本文件 (*.txt);;所有文件 (*)</source> <source>Markdown 文件 (*.md *.markdown *.mdown *.mkd);;文本文件 (*.txt);;所有文件 (*)</source>
<translation>Markdown files (*.md *.markdown *.mdown *.mkd);;Text files (*.txt);;All files (*)</translation> <translation>Markdown files (*.md *.markdown *.mdown *.mkd);;Text files (*.txt);;All files (*)</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="564" /> <location filename="../src/MainWindow.cpp" line="567" />
<source>无法打开文件</source> <source>无法打开文件</source>
<translation>Cannot Open File</translation> <translation>Cannot Open File</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="565" /> <location filename="../src/MainWindow.cpp" line="568" />
<source>读取「%1」失败:%2</source> <source>读取「%1」失败:%2</source>
<translation>Could not read "%1": %2</translation> <translation>Could not read "%1": %2</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="599" /> <location filename="../src/MainWindow.cpp" line="602" />
<source>另存为</source> <source>另存为</source>
<translation>Save As</translation> <translation>Save As</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="600" /> <location filename="../src/MainWindow.cpp" line="603" />
<source>Markdown 文件 (*.md);;文本文件 (*.txt);;所有文件 (*)</source> <source>Markdown 文件 (*.md);;文本文件 (*.txt);;所有文件 (*)</source>
<translation>Markdown files (*.md);;Text files (*.txt);;All files (*)</translation> <translation>Markdown files (*.md);;Text files (*.txt);;All files (*)</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="617" /> <location filename="../src/MainWindow.cpp" line="620" />
<source>导出 PDF</source> <source>导出 PDF</source>
<translation>Export PDF</translation> <translation>Export PDF</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="618" /> <location filename="../src/MainWindow.cpp" line="621" />
<source>PDF 文件 (*.pdf)</source> <source>PDF 文件 (*.pdf)</source>
<translation>PDF files (*.pdf)</translation> <translation>PDF files (*.pdf)</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="637" /> <location filename="../src/MainWindow.cpp" line="640" />
<source>导出失败</source> <source>导出失败</source>
<translation>Export Failed</translation> <translation>Export Failed</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="638" /> <location filename="../src/MainWindow.cpp" line="641" />
<source>没能写出「%1」,换个位置再试一次。</source> <source>没能写出「%1」,换个位置再试一次。</source>
<translation>Could not write "%1". Try another location.</translation> <translation>Could not write "%1". Try another location.</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="642" /> <location filename="../src/MainWindow.cpp" line="645" />
<source>已导出 %1</source> <source>已导出 %1</source>
<translation>Exported to %1</translation> <translation>Exported to %1</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="657" /> <location filename="../src/MainWindow.cpp" line="660" />
<location filename="../src/MainWindow.cpp" line="664" /> <location filename="../src/MainWindow.cpp" line="667" />
<location filename="../src/MainWindow.cpp" line="669" /> <location filename="../src/MainWindow.cpp" line="672" />
<source>无法保存文件</source> <source>无法保存文件</source>
<translation>Cannot Save File</translation> <translation>Cannot Save File</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="658" /> <location filename="../src/MainWindow.cpp" line="661" />
<source>写入「%1」失败:%2</source> <source>写入「%1」失败:%2</source>
<translation>Could not write "%1": %2</translation> <translation>Could not write "%1": %2</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="665" /> <location filename="../src/MainWindow.cpp" line="668" />
<source>写入「%1」时发生错误。</source> <source>写入「%1」时发生错误。</source>
<translation>An error occurred while writing "%1".</translation> <translation>An error occurred while writing "%1".</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="670" /> <location filename="../src/MainWindow.cpp" line="673" />
<source>提交「%1」失败:%2</source> <source>提交「%1」失败:%2</source>
<translation>Could not commit "%1": %2</translation> <translation>Could not commit "%1": %2</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="678" /> <location filename="../src/MainWindow.cpp" line="681" />
<source>已保存 %1</source> <source>已保存 %1</source>
<translation>Saved to %1</translation> <translation>Saved to %1</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="688" /> <location filename="../src/MainWindow.cpp" line="691" />
<source>VoiletMark</source> <source>VoiletMark</source>
<translation>VoiletMark</translation> <translation>VoiletMark</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="689" /> <location filename="../src/MainWindow.cpp" line="692" />
<source>「%1」有未保存的修改,是否保存?</source> <source>「%1」有未保存的修改,是否保存?</source>
<translation>"%1" has unsaved changes. Save them?</translation> <translation>"%1" has unsaved changes. Save them?</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="713" /> <location filename="../src/MainWindow.cpp" line="716" />
<source>%1[*] — VoiletMark</source> <source>%1[*] — VoiletMark</source>
<translation>%1[*] — VoiletMark</translation> <translation>%1[*] — VoiletMark</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="732" /> <location filename="../src/MainWindow.cpp" line="735" />
<source>行 %1,列 %2</source> <source>行 %1,列 %2</source>
<translation>Line %1, Col %2</translation> <translation>Line %1, Col %2</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="734" /> <location filename="../src/MainWindow.cpp" line="737" />
<source> 已选 %1 字</source> <source> 已选 %1 字</source>
<translation> %1 selected</translation> <translation> %1 selected</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="735" /> <location filename="../src/MainWindow.cpp" line="738" />
<source> %1 字</source> <source> %1 字</source>
<translation> %1 chars</translation> <translation> %1 chars</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="848" /> <location filename="../src/MainWindow.cpp" line="851" />
<source>&lt;h3&gt;VoiletMark %1&lt;/h3&gt;&lt;p&gt;一个轻量的跨平台 Markdown 编辑器。&lt;/p&gt;&lt;p&gt;基于 Qt %2 与 C++17 构建。&lt;/p&gt;</source> <source>&lt;h3&gt;VoiletMark %1&lt;/h3&gt;&lt;p&gt;一个轻量的跨平台 Markdown 编辑器。&lt;/p&gt;&lt;p&gt;基于 Qt %2 与 C++17 构建。&lt;/p&gt;</source>
<translation>&lt;h3&gt;VoiletMark %1&lt;/h3&gt;&lt;p&gt;A lightweight cross-platform Markdown editor.&lt;/p&gt;&lt;p&gt;Built with Qt %2 and C++17.&lt;/p&gt;</translation> <translation>&lt;h3&gt;VoiletMark %1&lt;/h3&gt;&lt;p&gt;A lightweight cross-platform Markdown editor.&lt;/p&gt;&lt;p&gt;Built with Qt %2 and C++17.&lt;/p&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="883" /> <location filename="../src/MainWindow.cpp" line="886" />
<source>(暂无记录)</source> <source>(暂无记录)</source>
<translation>(none)</translation> <translation>(none)</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="896" /> <location filename="../src/MainWindow.cpp" line="899" />
<source>清空列表</source> <source>清空列表</source>
<translation>Clear List</translation> <translation>Clear List</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="906" /> <location filename="../src/MainWindow.cpp" line="909" />
<source>打开文件夹</source> <source>打开文件夹</source>
<translation>Open Folder</translation> <translation>Open Folder</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="962" /> <location filename="../src/MainWindow.cpp" line="965" />
<source>文件 → 打开文件夹…</source> <source>文件 → 打开文件夹…</source>
<translation>File → Open Folder…</translation> <translation>File → Open Folder…</translation>
</message> </message>
<message> <message>
<location filename="../src/MainWindow.cpp" line="998" /> <location filename="../src/MainWindow.cpp" line="1001" />
<source>(没有 Markdown 文件)</source> <source>(没有 Markdown 文件)</source>
<translation>(no Markdown files)</translation> <translation>(no Markdown files)</translation>
</message> </message>
</context> </context>
<context>
<name>MarkdownEditor</name>
<message>
<location filename="../src/MarkdownEditor.cpp" line="549" />
<location filename="../src/MarkdownEditor.cpp" line="581" />
<source>已复制</source>
<translation>Copied</translation>
</message>
<message>
<location filename="../src/MarkdownEditor.cpp" line="549" />
<location filename="../src/MarkdownEditor.cpp" line="581" />
<source>复制</source>
<translation>Copy</translation>
</message>
</context>
<context> <context>
<name>QObject</name> <name>QObject</name>
<message> <message>