feat: v0.8.1 —— 代码框左上角加语言下拉菜单
- 编辑模式下每个代码框左上角显示当前语言(纯文本 / C++ / Python …), 点开是一个带勾选标记的语言菜单 - 选中后改写围栏上的语言标记(例如 ```cpp → ```python), 高亮立即跟着变;选「纯文本」则去掉语言标记 - 下拉与复制按钮都是画在编辑器视口里的,跟着滚动重算,源码模式下都不显示 - 「纯文本」加入中英文翻译 - 截图与 README 同步
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
一个轻量的跨平台 Markdown 编辑器,基于 **Qt 5 / C++17** 构建。
|
一个轻量的跨平台 Markdown 编辑器,基于 **Qt 5 / C++17** 构建。
|
||||||
|
|
||||||
当前版本 **0.8.0**。
|
当前版本 **0.8.1**。
|
||||||
|
|
||||||
## 界面
|
## 界面
|
||||||
|
|
||||||
@@ -29,6 +29,8 @@
|
|||||||
- 代码块按语言做语法高亮:C / C++ / Python / JavaScript / TypeScript /
|
- 代码块按语言做语法高亮:C / C++ / Python / JavaScript / TypeScript /
|
||||||
Java / Rust / Go / JSON / Bash / SQL / YAML,关键字、字符串、数字、
|
Java / Rust / Go / JSON / Bash / SQL / YAML,关键字、字符串、数字、
|
||||||
注释、预处理指令分色;不认识的标记语言就按等宽纯文本显示
|
注释、预处理指令分色;不认识的标记语言就按等宽纯文本显示
|
||||||
|
- 代码框**左上角**是语言下拉菜单,选一下就会改写围栏上的语言标记,
|
||||||
|
高亮立刻跟着变(选「纯文本」则去掉语言)
|
||||||
- `Tab` / `Shift+Tab` 整行缩进,选中多行时批量缩进
|
- `Tab` / `Shift+Tab` 整行缩进,选中多行时批量缩进
|
||||||
- 自动换行(`Alt+Z`)、`Ctrl+滚轮` 或 `Ctrl++` / `Ctrl+-` 缩放字号
|
- 自动换行(`Alt+Z`)、`Ctrl+滚轮` 或 `Ctrl++` / `Ctrl+-` 缩放字号
|
||||||
- 撤销 / 重做、剪切 / 复制 / 粘贴,菜单项会跟随可用状态自动灰显
|
- 撤销 / 重做、剪切 / 复制 / 粘贴,菜单项会跟随可用状态自动灰显
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
TARGET = VoiletMark
|
TARGET = VoiletMark
|
||||||
VERSION = 0.8.0
|
VERSION = 0.8.1
|
||||||
|
|
||||||
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 |
@@ -163,6 +163,34 @@ const LanguageDef *definition(Language language)
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
const QVector<LanguageEntry> &languageEntries()
|
||||||
|
{
|
||||||
|
static const QVector<LanguageEntry> entries = {
|
||||||
|
{Language::C, QStringLiteral("c"), QStringLiteral("C")},
|
||||||
|
{Language::Cpp, QStringLiteral("cpp"), QStringLiteral("C++")},
|
||||||
|
{Language::Python, QStringLiteral("python"), QStringLiteral("Python")},
|
||||||
|
{Language::JavaScript, QStringLiteral("js"), QStringLiteral("JavaScript")},
|
||||||
|
{Language::TypeScript, QStringLiteral("ts"), QStringLiteral("TypeScript")},
|
||||||
|
{Language::Java, QStringLiteral("java"), QStringLiteral("Java")},
|
||||||
|
{Language::Rust, QStringLiteral("rust"), QStringLiteral("Rust")},
|
||||||
|
{Language::Go, QStringLiteral("go"), QStringLiteral("Go")},
|
||||||
|
{Language::Json, QStringLiteral("json"), QStringLiteral("JSON")},
|
||||||
|
{Language::Bash, QStringLiteral("bash"), QStringLiteral("Bash")},
|
||||||
|
{Language::Sql, QStringLiteral("sql"), QStringLiteral("SQL")},
|
||||||
|
{Language::Yaml, QStringLiteral("yaml"), QStringLiteral("YAML")},
|
||||||
|
};
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString displayName(Language language)
|
||||||
|
{
|
||||||
|
for (const LanguageEntry &entry : languageEntries()) {
|
||||||
|
if (entry.language == language)
|
||||||
|
return entry.name;
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
Language fromInfoString(const QString &info)
|
Language fromInfoString(const QString &info)
|
||||||
{
|
{
|
||||||
QString name = info.trimmed().toLower();
|
QString name = info.trimmed().toLower();
|
||||||
|
|||||||
@@ -34,6 +34,17 @@ struct Span {
|
|||||||
Token token = Token::Keyword;
|
Token token = Token::Keyword;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LanguageEntry {
|
||||||
|
Language language;
|
||||||
|
QString info; // 写进围栏里的标记,例如 cpp
|
||||||
|
QString name; // 显示名,例如 C++
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 供语言下拉菜单使用(顺序即菜单顺序)。
|
||||||
|
const QVector<LanguageEntry> &languageEntries();
|
||||||
|
|
||||||
|
QString displayName(Language language);
|
||||||
|
|
||||||
/// 从 ```cpp 这类 info string 里识别语言(大小写不敏感)。
|
/// 从 ```cpp 这类 info string 里识别语言(大小写不敏感)。
|
||||||
Language fromInfoString(const QString &info);
|
Language fromInfoString(const QString &info);
|
||||||
|
|
||||||
|
|||||||
+133
-19
@@ -12,6 +12,7 @@
|
|||||||
#include <QFontMetricsF>
|
#include <QFontMetricsF>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
#include <QMenu>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@@ -56,6 +57,14 @@ bool isFenceLine(const QTextBlock &block)
|
|||||||
return block.isValid() && block.text().trimmed().startsWith(fenceMarker());
|
return block.isValid() && block.text().trimmed().startsWith(fenceMarker());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString fenceInfo(const QString &line)
|
||||||
|
{
|
||||||
|
QString info = line.trimmed();
|
||||||
|
static const QRegularExpression marker(QStringLiteral("^(?:```+|~~~+)"));
|
||||||
|
info.remove(marker);
|
||||||
|
return info.trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
class LineNumberArea : public QWidget
|
class LineNumberArea : public QWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -519,10 +528,23 @@ void MarkdownEditor::paintEvent(QPaintEvent *event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑模式:代码块套一个圆角框,右上角给个复制按钮。
|
// 编辑模式:代码块套圆角框,左上角选语言,右上角复制。
|
||||||
const ThemeColors &colors = ThemeManager::instance()->colors();
|
const ThemeColors &colors = ThemeManager::instance()->colors();
|
||||||
const QRectF visible(event->rect());
|
const QRectF visible(event->rect());
|
||||||
|
|
||||||
|
auto drawBadge = [&](const QRectF &badge, const QString &label, bool hovered, bool emphasized) {
|
||||||
|
if (!badge.isValid())
|
||||||
|
return;
|
||||||
|
QFont small = font();
|
||||||
|
small.setPointSizeF(qMax(7.0, font().pointSizeF() - 2.0));
|
||||||
|
painter.setFont(small);
|
||||||
|
painter.setPen(QPen(hovered ? colors.accent : colors.border, 1));
|
||||||
|
painter.setBrush(colors.editorBase);
|
||||||
|
painter.drawRoundedRect(badge, 4, 4);
|
||||||
|
painter.setPen(emphasized ? colors.accent : colors.placeholder);
|
||||||
|
painter.drawText(badge, Qt::AlignCenter, label);
|
||||||
|
};
|
||||||
|
|
||||||
for (int index = 0; index < m_codeFences.size(); ++index) {
|
for (int index = 0; index < m_codeFences.size(); ++index) {
|
||||||
const QRectF rect = codeFenceRect(index);
|
const QRectF rect = codeFenceRect(index);
|
||||||
if (!rect.isValid() || !rect.intersects(visible))
|
if (!rect.isValid() || !rect.intersects(visible))
|
||||||
@@ -532,21 +554,12 @@ void MarkdownEditor::paintEvent(QPaintEvent *event)
|
|||||||
painter.setPen(QPen(colors.border, 1));
|
painter.setPen(QPen(colors.border, 1));
|
||||||
painter.drawRoundedRect(rect.adjusted(0.5, 0.5, -0.5, -0.5), 6, 6);
|
painter.drawRoundedRect(rect.adjusted(0.5, 0.5, -0.5, -0.5), 6, 6);
|
||||||
|
|
||||||
const QRectF button = copyButtonRect(index);
|
drawBadge(languageRect(index), languageLabel(index) + QStringLiteral(" \u25be"),
|
||||||
if (!button.isValid())
|
m_hoveredLanguage == index, false);
|
||||||
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);
|
const bool copied = (m_copiedFence == index);
|
||||||
painter.setPen(QPen(hovered ? colors.accent : colors.border, 1));
|
drawBadge(copyButtonRect(index), copied ? tr("已复制") : tr("复制"),
|
||||||
painter.setBrush(colors.editorBase);
|
m_hoveredFence == index, copied);
|
||||||
painter.drawRoundedRect(button, 4, 4);
|
|
||||||
painter.setPen(copied ? colors.accent : colors.placeholder);
|
|
||||||
painter.drawText(button, Qt::AlignCenter, copied ? tr("已复制") : tr("复制"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,6 +582,87 @@ QRectF MarkdownEditor::codeFenceRect(int index) const
|
|||||||
return QRectF(3, top, viewport()->width() - 6, bottom - top);
|
return QRectF(3, top, viewport()->width() - 6, bottom - top);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRectF MarkdownEditor::languageRect(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 = languageLabel(index) + QStringLiteral(" \u25be");
|
||||||
|
const qreal width = metrics.horizontalAdvance(label) + 16;
|
||||||
|
const qreal height = metrics.height() + 6;
|
||||||
|
return QRectF(rect.left() + 8, rect.top() + 6, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MarkdownEditor::languageLabel(int index) const
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_codeFences.size())
|
||||||
|
return QString();
|
||||||
|
const CodeHighlight::Language language = m_codeFences.at(index).language;
|
||||||
|
if (language == CodeHighlight::Language::None)
|
||||||
|
return tr("纯文本");
|
||||||
|
return CodeHighlight::displayName(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkdownEditor::showLanguageMenu(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_codeFences.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QMenu menu(this);
|
||||||
|
|
||||||
|
QAction *plain = menu.addAction(tr("纯文本"));
|
||||||
|
plain->setData(QString());
|
||||||
|
plain->setCheckable(true);
|
||||||
|
plain->setChecked(m_codeFences.at(index).language == CodeHighlight::Language::None);
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
for (const CodeHighlight::LanguageEntry &entry : CodeHighlight::languageEntries()) {
|
||||||
|
QAction *action = menu.addAction(entry.name);
|
||||||
|
action->setData(entry.info);
|
||||||
|
action->setCheckable(true);
|
||||||
|
action->setChecked(entry.language == m_codeFences.at(index).language);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRectF badge = languageRect(index);
|
||||||
|
const QPoint origin = viewport()->mapToGlobal(badge.bottomLeft().toPoint());
|
||||||
|
QAction *chosen = menu.exec(origin);
|
||||||
|
if (!chosen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setCodeFenceLanguage(index, chosen->data().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkdownEditor::setCodeFenceLanguage(int index, const QString &info)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_codeFences.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int blockNumber = m_codeFences.at(index).open;
|
||||||
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
||||||
|
if (!block.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
static const QRegularExpression marker(QStringLiteral("^\\s{0,3}(?:```+|~~~+)"));
|
||||||
|
const QRegularExpressionMatch match = marker.match(block.text());
|
||||||
|
if (!match.hasMatch())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QString newText = match.captured(0) + info;
|
||||||
|
if (newText == block.text())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QTextCursor cursor(block);
|
||||||
|
cursor.beginEditBlock();
|
||||||
|
cursor.setPosition(block.position());
|
||||||
|
cursor.setPosition(block.position() + block.text().length(), QTextCursor::KeepAnchor);
|
||||||
|
cursor.insertText(newText);
|
||||||
|
cursor.endEditBlock();
|
||||||
|
}
|
||||||
|
|
||||||
QRectF MarkdownEditor::copyButtonRect(int index) const
|
QRectF MarkdownEditor::copyButtonRect(int index) const
|
||||||
{
|
{
|
||||||
const QRectF rect = codeFenceRect(index);
|
const QRectF rect = codeFenceRect(index);
|
||||||
@@ -606,6 +700,7 @@ void MarkdownEditor::rebuildCodeFences()
|
|||||||
static const QRegularExpression fencePattern(QStringLiteral("^\\s{0,3}(?:```|~~~)"));
|
static const QRegularExpression fencePattern(QStringLiteral("^\\s{0,3}(?:```|~~~)"));
|
||||||
bool inFence = false;
|
bool inFence = false;
|
||||||
int open = -1;
|
int open = -1;
|
||||||
|
QString openInfo;
|
||||||
|
|
||||||
for (QTextBlock block = document()->firstBlock(); block.isValid(); block = block.next()) {
|
for (QTextBlock block = document()->firstBlock(); block.isValid(); block = block.next()) {
|
||||||
if (!fencePattern.match(block.text()).hasMatch())
|
if (!fencePattern.match(block.text()).hasMatch())
|
||||||
@@ -613,10 +708,13 @@ void MarkdownEditor::rebuildCodeFences()
|
|||||||
if (!inFence) {
|
if (!inFence) {
|
||||||
inFence = true;
|
inFence = true;
|
||||||
open = block.blockNumber();
|
open = block.blockNumber();
|
||||||
|
openInfo = fenceInfo(block.text());
|
||||||
} else {
|
} else {
|
||||||
CodeFence fence;
|
CodeFence fence;
|
||||||
fence.open = open;
|
fence.open = open;
|
||||||
fence.close = block.blockNumber();
|
fence.close = block.blockNumber();
|
||||||
|
fence.info = openInfo;
|
||||||
|
fence.language = CodeHighlight::fromInfoString(openInfo);
|
||||||
m_codeFences.append(fence);
|
m_codeFences.append(fence);
|
||||||
inFence = false;
|
inFence = false;
|
||||||
open = -1;
|
open = -1;
|
||||||
@@ -625,6 +723,8 @@ void MarkdownEditor::rebuildCodeFences()
|
|||||||
if (inFence && open >= 0) {
|
if (inFence && open >= 0) {
|
||||||
CodeFence fence;
|
CodeFence fence;
|
||||||
fence.open = open;
|
fence.open = open;
|
||||||
|
fence.info = openInfo;
|
||||||
|
fence.language = CodeHighlight::fromInfoString(openInfo);
|
||||||
m_codeFences.append(fence);
|
m_codeFences.append(fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,17 +737,25 @@ void MarkdownEditor::mouseMoveEvent(QMouseEvent *event)
|
|||||||
if (m_sourceMode)
|
if (m_sourceMode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int hovered = -1;
|
int hoveredCopy = -1;
|
||||||
|
int hoveredLanguage = -1;
|
||||||
for (int index = 0; index < m_codeFences.size(); ++index) {
|
for (int index = 0; index < m_codeFences.size(); ++index) {
|
||||||
|
if (languageRect(index).contains(event->pos())) {
|
||||||
|
hoveredLanguage = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (copyButtonRect(index).contains(event->pos())) {
|
if (copyButtonRect(index).contains(event->pos())) {
|
||||||
hovered = index;
|
hoveredCopy = index;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hovered != m_hoveredFence) {
|
if (hoveredCopy != m_hoveredFence || hoveredLanguage != m_hoveredLanguage) {
|
||||||
m_hoveredFence = hovered;
|
m_hoveredFence = hoveredCopy;
|
||||||
viewport()->setCursor(hovered >= 0 ? Qt::PointingHandCursor : Qt::IBeamCursor);
|
m_hoveredLanguage = hoveredLanguage;
|
||||||
|
viewport()->setCursor((hoveredCopy >= 0 || hoveredLanguage >= 0)
|
||||||
|
? Qt::PointingHandCursor
|
||||||
|
: Qt::IBeamCursor);
|
||||||
viewport()->update();
|
viewport()->update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -655,6 +763,12 @@ void MarkdownEditor::mouseMoveEvent(QMouseEvent *event)
|
|||||||
void MarkdownEditor::mousePressEvent(QMouseEvent *event)
|
void MarkdownEditor::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (!m_sourceMode && event->button() == Qt::LeftButton) {
|
if (!m_sourceMode && event->button() == Qt::LeftButton) {
|
||||||
|
for (int index = 0; index < m_codeFences.size(); ++index) {
|
||||||
|
if (languageRect(index).contains(event->pos())) {
|
||||||
|
showLanguageMenu(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int index = 0; index < m_codeFences.size(); ++index) {
|
for (int index = 0; index < m_codeFences.size(); ++index) {
|
||||||
if (!copyButtonRect(index).contains(event->pos()))
|
if (!copyButtonRect(index).contains(event->pos()))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "CodeHighlight.h"
|
||||||
|
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
@@ -66,7 +68,11 @@ private:
|
|||||||
void indentSelection(bool outdent);
|
void indentSelection(bool outdent);
|
||||||
QRectF codeFenceRect(int index) const;
|
QRectF codeFenceRect(int index) const;
|
||||||
QRectF copyButtonRect(int index) const;
|
QRectF copyButtonRect(int index) const;
|
||||||
|
QRectF languageRect(int index) const;
|
||||||
QString codeFenceText(int index) const;
|
QString codeFenceText(int index) const;
|
||||||
|
QString languageLabel(int index) const;
|
||||||
|
void showLanguageMenu(int index);
|
||||||
|
void setCodeFenceLanguage(int index, const QString &info);
|
||||||
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);
|
||||||
@@ -76,6 +82,8 @@ private:
|
|||||||
struct CodeFence {
|
struct CodeFence {
|
||||||
int open = -1; // 开围栏所在行
|
int open = -1; // 开围栏所在行
|
||||||
int close = -1; // 闭围栏所在行,未闭合时为 -1
|
int close = -1; // 闭围栏所在行,未闭合时为 -1
|
||||||
|
QString info; // 围栏上的语言标记
|
||||||
|
CodeHighlight::Language language = CodeHighlight::Language::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
QWidget *m_lineNumberArea = nullptr;
|
QWidget *m_lineNumberArea = nullptr;
|
||||||
@@ -87,5 +95,6 @@ private:
|
|||||||
QVector<CodeFence> m_codeFences;
|
QVector<CodeFence> m_codeFences;
|
||||||
QTimer *m_fenceTimer = nullptr;
|
QTimer *m_fenceTimer = nullptr;
|
||||||
int m_hoveredFence = -1;
|
int m_hoveredFence = -1;
|
||||||
|
int m_hoveredLanguage = -1;
|
||||||
int m_copiedFence = -1;
|
int m_copiedFence = -1;
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
@@ -372,37 +372,43 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>MarkdownEditor</name>
|
<name>MarkdownEditor</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/MarkdownEditor.cpp" line="549" />
|
<location filename="../src/MarkdownEditor.cpp" line="561" />
|
||||||
<location filename="../src/MarkdownEditor.cpp" line="581" />
|
<location filename="../src/MarkdownEditor.cpp" line="675" />
|
||||||
<source>已复制</source>
|
<source>已复制</source>
|
||||||
<translation>Copied</translation>
|
<translation>Copied</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/MarkdownEditor.cpp" line="549" />
|
<location filename="../src/MarkdownEditor.cpp" line="561" />
|
||||||
<location filename="../src/MarkdownEditor.cpp" line="581" />
|
<location filename="../src/MarkdownEditor.cpp" line="675" />
|
||||||
<source>复制</source>
|
<source>复制</source>
|
||||||
<translation>Copy</translation>
|
<translation>Copy</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../src/MarkdownEditor.cpp" line="606" />
|
||||||
|
<location filename="../src/MarkdownEditor.cpp" line="617" />
|
||||||
|
<source>纯文本</source>
|
||||||
|
<translation>Plain text</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>QObject</name>
|
<name>QObject</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/ThemeManager.cpp" line="354" />
|
<location filename="../src/ThemeManager.cpp" line="364" />
|
||||||
<source>浅色</source>
|
<source>浅色</source>
|
||||||
<translation>Light</translation>
|
<translation>Light</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/ThemeManager.cpp" line="355" />
|
<location filename="../src/ThemeManager.cpp" line="365" />
|
||||||
<source>深色</source>
|
<source>深色</source>
|
||||||
<translation>Dark</translation>
|
<translation>Dark</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/ThemeManager.cpp" line="356" />
|
<location filename="../src/ThemeManager.cpp" line="366" />
|
||||||
<source>按时段自动</source>
|
<source>按时段自动</source>
|
||||||
<translation>Scheduled</translation>
|
<translation>Scheduled</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/ThemeManager.cpp" line="359" />
|
<location filename="../src/ThemeManager.cpp" line="369" />
|
||||||
<source>跟随系统</source>
|
<source>跟随系统</source>
|
||||||
<translation>Follow System</translation>
|
<translation>Follow System</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
Reference in New Issue
Block a user