diff --git a/README.md b/README.md index 65a492f..7979605 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,13 @@ 一个轻量的跨平台 Markdown 编辑器,基于 **Qt 5 / C++17** 构建。 -当前版本 **0.11.0**。 +当前版本 **0.12.0**。 ## 界面 +窗口从上到下依次是菜单栏、工具栏、侧栏 + 编辑区、状态栏。工具栏固定放 +**新建 / 打开 / 保存**,以及 **最近文件**、**最近目录** 两个下拉菜单。 + | 浅色 · 编辑模式 | 深色 · 编辑模式 | | --- | --- | | ![浅色主题](docs/screenshot-light.png) | ![深色主题](docs/screenshot-dark.png) | @@ -121,11 +124,27 @@ Windows / macOS 保留系统原生对话框。 ### 文件 -- 新建、打开、保存、另存为、最近打开(最多 8 条) +- 新建、打开、保存、另存为 +- **最近文件** / **最近目录**:各记 8 条,文件菜单和工具栏上的下拉菜单都能打开, + 两个列表可以分别清空 +- **启动时自动打开上次编辑的文件**;文件已经被删掉就退回欢迎文档 - 保留原文件的 **BOM** 与 **CRLF** 换行风格,保存时原子写入(`QSaveFile`) - 关闭或切换文件时提示未保存的修改 - 记忆窗口位置、字号、换行开关、侧栏状态等设置 +### 配置文件 + +所有设置都写进同一个 INI 文件,换机器时把它拷过去就行: + +| 平台 | 路径 | +| --- | --- | +| Windows | `%LOCALAPPDATA%\VoiletMark\VoiletMark\config.ini` | +| Linux | `~/.config/VoiletMark/VoiletMark/config.ini` | +| macOS | `~/Library/Preferences/VoiletMark/VoiletMark/config.ini` | + +确切路径也显示在 **帮助 → 关于 VoiletMark** 里。文件里记着主题、语言、字号、 +自动换行、侧栏状态、最近文件 / 最近目录、上次打开的文件和上次的文件夹。 + ### 主题 四种模式: @@ -236,7 +255,8 @@ src/ ├── main.cpp 入口:命令行解析、Fusion 样式、主题 / 语言初始化 ├── ThemeManager.{h,cpp} 四种主题模式 + 系统检测 + 调色板 / QSS 生成 ├── LanguageManager.{h,cpp} 中英文切换 -├── MainWindow.{h,cpp} 菜单、文件读写、侧栏、Git 工具栏、状态栏、设置 +├── AppConfig.{h,cpp} 所有设置共用的 config.ini +├── MainWindow.{h,cpp} 菜单、工具栏、文件读写、侧栏、Git 工具栏、状态栏、设置 ├── MarkdownEditor.{h,cpp} 编辑器控件:行号、缩进、代码框装饰、源码模式 ├── MarkdownHighlighter.{h,cpp} Markdown 着色与就地渲染 ├── CodeHighlight.{h,cpp} 多语言语法高亮 tokenizer diff --git a/VoiletMark.pro b/VoiletMark.pro index f73dc4a..074c199 100644 --- a/VoiletMark.pro +++ b/VoiletMark.pro @@ -1,6 +1,6 @@ TEMPLATE = app TARGET = VoiletMark -VERSION = 0.11.0 +VERSION = 0.12.0 QT += core gui widgets @@ -23,6 +23,7 @@ SOURCES += \ src/MarkdownEditor.cpp \ src/MarkdownHighlighter.cpp \ src/MarkdownRenderer.cpp \ + src/AppConfig.cpp \ src/CodeHighlight.cpp \ src/GitRunner.cpp \ src/LanguageManager.cpp \ @@ -33,6 +34,7 @@ HEADERS += \ src/MarkdownEditor.h \ src/MarkdownHighlighter.h \ src/MarkdownRenderer.h \ + src/AppConfig.h \ src/CodeHighlight.h \ src/GitRunner.h \ src/LanguageManager.h \ diff --git a/docs/screenshot-dark.png b/docs/screenshot-dark.png index aa5ac32..02056b6 100644 Binary files a/docs/screenshot-dark.png and b/docs/screenshot-dark.png differ diff --git a/docs/screenshot-light.png b/docs/screenshot-light.png index a5af041..90098dd 100644 Binary files a/docs/screenshot-light.png and b/docs/screenshot-light.png differ diff --git a/docs/screenshot-source.png b/docs/screenshot-source.png index 8fc9592..b004c9a 100644 Binary files a/docs/screenshot-source.png and b/docs/screenshot-source.png differ diff --git a/src/AppConfig.cpp b/src/AppConfig.cpp new file mode 100644 index 0000000..e04601d --- /dev/null +++ b/src/AppConfig.cpp @@ -0,0 +1,34 @@ +#include "AppConfig.h" + +#include +#include +#include + +namespace { + +QString configFilePath() +{ + const QString directory = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + return QDir(directory).filePath(QStringLiteral("config.ini")); +} + +} // namespace + +QString AppConfig::filePath() +{ + return configFilePath(); +} + +QSettings &AppConfig::settings() +{ + static QSettings *instance = nullptr; + if (!instance) { + const QString path = configFilePath(); + QDir().mkpath(QFileInfo(path).absolutePath()); + instance = new QSettings(path, QSettings::IniFormat); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + instance->setIniCodec("UTF-8"); +#endif + } + return *instance; +} diff --git a/src/AppConfig.h b/src/AppConfig.h new file mode 100644 index 0000000..d2a84e8 --- /dev/null +++ b/src/AppConfig.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +/// 统一的应用配置:所有设置写进同一个 INI 文件, +/// 而不是散落在系统注册表 / 各自的配置目录里。 +/// +/// Linux: ~/.config/VoiletMark/VoiletMark/config.ini +/// Windows: %LOCALAPPDATA%/VoiletMark/VoiletMark/config.ini +/// macOS: ~/Library/Preferences/VoiletMark/VoiletMark/config.ini +namespace AppConfig { + +QSettings &settings(); +QString filePath(); + +} // namespace AppConfig diff --git a/src/LanguageManager.cpp b/src/LanguageManager.cpp index dadad9a..49974dc 100644 --- a/src/LanguageManager.cpp +++ b/src/LanguageManager.cpp @@ -5,6 +5,8 @@ #include #include +#include "AppConfig.h" + LanguageManager *LanguageManager::instance() { static LanguageManager *manager = new LanguageManager(); @@ -30,7 +32,7 @@ QString LanguageManager::displayName(const QString &code) void LanguageManager::initialize() { - QSettings settings; + QSettings &settings = AppConfig::settings(); QString code = settings.value(QStringLiteral("ui/language")).toString(); if (!availableLanguages().contains(code)) { code = QLocale::system().language() == QLocale::English @@ -77,6 +79,6 @@ void LanguageManager::setLanguage(const QString &code) } m_language = normalized; - QSettings().setValue(QStringLiteral("ui/language"), m_language); + AppConfig::settings().setValue(QStringLiteral("ui/language"), m_language); emit languageChanged(); } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 871d44d..55f2e8a 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1,5 +1,6 @@ #include "MainWindow.h" +#include "AppConfig.h" #include "GitRunner.h" #include "LanguageManager.h" #include "MarkdownEditor.h" @@ -43,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -176,8 +178,9 @@ MainWindow::MainWindow(QWidget *parent) connect(m_git, &GitRunner::finished, this, &MainWindow::onGitFinished); createActions(); - createSidebar(); createMenus(); + createToolBar(); + createSidebar(); createStatusBar(); loadSettings(); refreshRecentMenu(); @@ -193,12 +196,51 @@ MainWindow::MainWindow(QWidget *parent) connect(LanguageManager::instance(), &LanguageManager::languageChanged, this, &MainWindow::retranslateUi); - showWelcomeDocument(); + // 恢复上次打开的文件;没有记录或文件已不存在时显示欢迎文档 + const QString lastFile = + AppConfig::settings().value(QStringLiteral("session/lastFile")).toString(); + if (!lastFile.isEmpty() && QFileInfo::exists(lastFile)) { + openPath(lastFile); + } else { + showWelcomeDocument(); + } + updateEditActionStates(); updateStatusBar(); m_editor->setFocus(); } +void MainWindow::createToolBar() +{ + m_toolBar = addToolBar(tr("常用")); + m_toolBar->setObjectName(QStringLiteral("mainToolBar")); + m_toolBar->setMovable(false); + m_toolBar->setToolButtonStyle(Qt::ToolButtonTextOnly); + m_toolBar->setIconSize(QSize(16, 16)); + + m_toolBar->addAction(m_newAction); + m_toolBar->addAction(m_openAction); + m_toolBar->addAction(m_saveAction); + m_toolBar->addSeparator(); + + const auto makeMenuButton = [this](const QString &text, QMenu **target) { + QToolButton *button = new QToolButton(m_toolBar); + button->setText(text); + button->setPopupMode(QToolButton::InstantPopup); + button->setToolButtonStyle(Qt::ToolButtonTextOnly); + button->setCursor(Qt::PointingHandCursor); + + QMenu *menu = new QMenu(button); + button->setMenu(menu); + m_toolBar->addWidget(button); + *target = menu; + return button; + }; + + m_toolRecentFilesButton = makeMenuButton(tr("最近文件"), &m_toolRecentFilesMenu); + m_toolRecentFoldersButton = makeMenuButton(tr("最近目录"), &m_toolRecentFoldersMenu); +} + void MainWindow::createActions() { m_newAction = new QAction(tr("新建"), this); @@ -286,7 +328,7 @@ void MainWindow::createActions() const QSignalBlocker blocker(m_sidebarButton); m_sidebarButton->setChecked(visible); } - QSettings().setValue(QStringLiteral("window/sidebarVisible"), visible); + AppConfig::settings().setValue(QStringLiteral("window/sidebarVisible"), visible); }); m_sourceModeAction = new QAction(tr("源代码模式"), this); @@ -443,7 +485,8 @@ void MainWindow::createMenus() m_fileMenu->addAction(m_newAction); m_fileMenu->addAction(m_openAction); m_fileMenu->addAction(m_openFolderAction); - m_recentMenu = m_fileMenu->addMenu(tr("最近打开")); + m_recentMenu = m_fileMenu->addMenu(tr("最近文件")); + m_recentFolderMenu = m_fileMenu->addMenu(tr("最近目录")); m_fileMenu->addSeparator(); m_fileMenu->addAction(m_saveAction); m_fileMenu->addAction(m_saveAsAction); @@ -556,7 +599,7 @@ void MainWindow::createStatusBar() void MainWindow::loadSettings() { - QSettings settings; + QSettings &settings = AppConfig::settings(); const QByteArray geometry = settings.value(QStringLiteral("window/geometry")).toByteArray(); if (!geometry.isEmpty()) @@ -604,7 +647,7 @@ void MainWindow::loadSettings() void MainWindow::saveSettings() { - QSettings settings; + QSettings &settings = AppConfig::settings(); settings.setValue(QStringLiteral("window/geometry"), saveGeometry()); settings.setValue(QStringLiteral("editor/fontSize"), m_editor->editorFontPointSize()); settings.setValue(QStringLiteral("editor/wordWrap"), m_wrapAction->isChecked()); @@ -612,6 +655,7 @@ void MainWindow::saveSettings() settings.setValue(QStringLiteral("files/recent"), m_recentFiles); settings.setValue(QStringLiteral("files/folder"), m_folderPath); settings.setValue(QStringLiteral("window/sidebarVisible"), m_sidebar->isVisible()); + settings.sync(); } void MainWindow::closeEvent(QCloseEvent *event) @@ -882,6 +926,7 @@ bool MainWindow::maybeSave() void MainWindow::setCurrentFile(const QString &path) { m_currentPath = path; + AppConfig::settings().setValue(QStringLiteral("session/lastFile"), path); if (!path.isEmpty()) { m_lastDirectory = QFileInfo(path).absolutePath(); setWindowFilePath(path); @@ -926,7 +971,7 @@ void MainWindow::changeZoom(int direction) const int size = (direction == 0) ? kDefaultFontSize : m_editor->editorFontPointSize() + direction; m_editor->setEditorFontPointSize(size); - QSettings().setValue(QStringLiteral("editor/fontSize"), m_editor->editorFontPointSize()); + AppConfig::settings().setValue(QStringLiteral("editor/fontSize"), m_editor->editorFontPointSize()); } void MainWindow::resetZoom() @@ -937,7 +982,7 @@ void MainWindow::resetZoom() void MainWindow::setWordWrapEnabled(bool enabled) { m_editor->setLineWrapMode(enabled ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap); - QSettings().setValue(QStringLiteral("editor/wordWrap"), enabled); + AppConfig::settings().setValue(QStringLiteral("editor/wordWrap"), enabled); } void MainWindow::applyThemeMode(int mode) @@ -957,7 +1002,14 @@ void MainWindow::retranslateUi() m_viewMenu->setTitle(tr("视图")); m_formatMenu->setTitle(tr("格式")); m_helpMenu->setTitle(tr("帮助")); - m_recentMenu->setTitle(tr("最近打开")); + m_recentMenu->setTitle(tr("最近文件")); + m_recentFolderMenu->setTitle(tr("最近目录")); + if (m_toolBar) + m_toolBar->setWindowTitle(tr("常用")); + if (m_toolRecentFilesButton) + m_toolRecentFilesButton->setText(tr("最近文件")); + if (m_toolRecentFoldersButton) + m_toolRecentFoldersButton->setText(tr("最近目录")); m_themeMenu->setTitle(tr("主题")); m_headingMenu->setTitle(tr("标题")); m_languageMenu->setTitle(tr("语言")); @@ -1008,7 +1060,7 @@ void MainWindow::retranslateUi() m_languageChineseAction->setChecked(language == QLatin1String("zh_CN")); m_languageEnglishAction->setChecked(language == QLatin1String("en")); - // 侧栏、最近打开、欢迎文档都跟着换 + // 侧栏、最近文件、欢迎文档都跟着换 m_displayedFolder.clear(); refreshRecentMenu(); refreshFolder(); @@ -1034,6 +1086,8 @@ void MainWindow::showAbout() "

基于 Qt %2 与 C++17 构建。

") .arg(QCoreApplication::applicationVersion(), QString::fromLatin1(qVersion()))); + box.setInformativeText( + tr("配置文件:%1").arg(QDir::toNativeSeparators(AppConfig::filePath()))); box.setStandardButtons(QMessageBox::Ok); box.exec(); } @@ -1045,45 +1099,94 @@ void MainWindow::addRecentFile(const QString &path) m_recentFiles.prepend(absolutePath); while (m_recentFiles.size() > kMaxRecentFiles) m_recentFiles.removeLast(); + AppConfig::settings().setValue(QStringLiteral("files/recent"), m_recentFiles); refreshRecentMenu(); - QSettings().setValue(QStringLiteral("files/recent"), m_recentFiles); } void MainWindow::loadRecentFiles() { - m_recentFiles = QSettings().value(QStringLiteral("files/recent")).toStringList(); + m_recentFiles = AppConfig::settings().value(QStringLiteral("files/recent")).toStringList(); m_recentFiles.removeDuplicates(); while (m_recentFiles.size() > kMaxRecentFiles) m_recentFiles.removeLast(); + + m_recentFolders = + AppConfig::settings().value(QStringLiteral("files/recentFolders")).toStringList(); + m_recentFolders.removeDuplicates(); + while (m_recentFolders.size() > kMaxRecentFiles) + m_recentFolders.removeLast(); } void MainWindow::refreshRecentMenu() { - if (!m_recentMenu) + fillRecentFilesMenu(m_recentMenu); + fillRecentFilesMenu(m_toolRecentFilesMenu); + fillRecentFoldersMenu(m_recentFolderMenu); + fillRecentFoldersMenu(m_toolRecentFoldersMenu); +} + +void MainWindow::fillRecentFilesMenu(QMenu *menu) +{ + if (!menu) return; - m_recentMenu->clear(); + menu->clear(); if (m_recentFiles.isEmpty()) { - QAction *empty = m_recentMenu->addAction(tr("(暂无记录)")); + QAction *empty = menu->addAction(tr("(暂无记录)")); empty->setEnabled(false); return; } for (const QString &path : m_recentFiles) { - QAction *action = m_recentMenu->addAction(QDir::toNativeSeparators(path)); + QAction *action = menu->addAction(QDir::toNativeSeparators(path)); action->setToolTip(path); - connect(action, &QAction::triggered, this, [this, path] { - openPath(path); - }); + connect(action, &QAction::triggered, this, [this, path] { openPath(path); }); } - m_recentMenu->addSeparator(); - m_recentMenu->addAction(tr("清空列表"), this, [this] { + menu->addSeparator(); + menu->addAction(tr("清空列表"), this, [this] { m_recentFiles.clear(); - QSettings().setValue(QStringLiteral("files/recent"), m_recentFiles); + AppConfig::settings().setValue(QStringLiteral("files/recent"), m_recentFiles); refreshRecentMenu(); }); } +void MainWindow::fillRecentFoldersMenu(QMenu *menu) +{ + if (!menu) + return; + + menu->clear(); + if (m_recentFolders.isEmpty()) { + QAction *empty = menu->addAction(tr("(暂无记录)")); + empty->setEnabled(false); + return; + } + + for (const QString &path : m_recentFolders) { + QAction *action = menu->addAction(QDir::toNativeSeparators(path)); + action->setToolTip(path); + connect(action, &QAction::triggered, this, [this, path] { openFolder(path); }); + } + menu->addSeparator(); + menu->addAction(tr("清空列表"), this, [this] { + m_recentFolders.clear(); + AppConfig::settings().setValue(QStringLiteral("files/recentFolders"), m_recentFolders); + refreshRecentMenu(); + }); +} + +void MainWindow::addRecentFolder(const QString &path) +{ + const QString absolutePath = QFileInfo(path).absoluteFilePath(); + m_recentFolders.removeAll(absolutePath); + m_recentFolders.prepend(absolutePath); + while (m_recentFolders.size() > kMaxRecentFiles) + m_recentFolders.removeLast(); + + AppConfig::settings().setValue(QStringLiteral("files/recentFolders"), m_recentFolders); + refreshRecentMenu(); +} + void MainWindow::chooseFolder() { const QString directory = QFileDialog::getExistingDirectory( @@ -1101,7 +1204,8 @@ void MainWindow::openFolder(const QString &path) m_folderPath = info.absoluteFilePath(); m_lastDirectory = m_folderPath; - QSettings().setValue(QStringLiteral("files/folder"), m_folderPath); + AppConfig::settings().setValue(QStringLiteral("files/folder"), m_folderPath); + addRecentFolder(m_folderPath); refreshFolder(); updateGitState(); @@ -1243,7 +1347,7 @@ void MainWindow::setSourceModeEnabled(bool enabled) m_sourceModeAction->setChecked(enabled); } - QSettings().setValue(QStringLiteral("editor/sourceMode"), enabled); + AppConfig::settings().setValue(QStringLiteral("editor/sourceMode"), enabled); } // ---------------------------------------------------------------- Git @@ -1356,7 +1460,7 @@ void MainWindow::switchSidebarMode(int mode) if (m_sidebarMode == 1) rebuildToc(); - QSettings().setValue(QStringLiteral("window/sidebarMode"), m_sidebarMode); + AppConfig::settings().setValue(QStringLiteral("window/sidebarMode"), m_sidebarMode); } void MainWindow::rebuildToc() diff --git a/src/MainWindow.h b/src/MainWindow.h index 2dc2df2..a766d8b 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -10,6 +10,7 @@ class QDockWidget; class QLabel; class QPushButton; class QStackedWidget; +class QToolBar; class QToolButton; class GitRunner; class QListWidget; @@ -35,6 +36,7 @@ private: enum class Eol { Lf, Crlf }; void createActions(); + void createToolBar(); void createSidebar(); void createMenus(); void createStatusBar(); @@ -51,7 +53,10 @@ private: void showWelcomeDocument(); void addRecentFile(const QString &path); + void addRecentFolder(const QString &path); void refreshRecentMenu(); + void fillRecentFilesMenu(QMenu *menu); + void fillRecentFoldersMenu(QMenu *menu); void loadRecentFiles(); void chooseFolder(); @@ -140,6 +145,7 @@ private: Eol m_eol = Eol::Lf; bool m_hasBom = false; QStringList m_recentFiles; + QStringList m_recentFolders; QString m_lastDirectory; QAction *m_newAction = nullptr; @@ -173,6 +179,12 @@ private: QAction *m_headingActions[6] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; QMenu *m_recentMenu = nullptr; + QMenu *m_recentFolderMenu = nullptr; + QMenu *m_toolRecentFilesMenu = nullptr; + QMenu *m_toolRecentFoldersMenu = nullptr; + QToolButton *m_toolRecentFilesButton = nullptr; + QToolButton *m_toolRecentFoldersButton = nullptr; + QToolBar *m_toolBar = nullptr; QMenu *m_fileMenu = nullptr; QMenu *m_editMenu = nullptr; QMenu *m_viewMenu = nullptr; diff --git a/src/MarkdownEditor.cpp b/src/MarkdownEditor.cpp index 2b5adbc..ef72836 100644 --- a/src/MarkdownEditor.cpp +++ b/src/MarkdownEditor.cpp @@ -105,6 +105,8 @@ MarkdownEditor::MarkdownEditor(QWidget *parent) setAcceptDrops(true); viewport()->setMouseTracking(true); document()->setDocumentMargin(18); + // setDocumentMargin() 会把文档标记为已修改,新建编辑器时清掉 + document()->setModified(false); m_lineNumberArea = new LineNumberArea(this); m_highlighter = new MarkdownHighlighter(document()); diff --git a/src/ThemeManager.cpp b/src/ThemeManager.cpp index df96e0e..280bce1 100644 --- a/src/ThemeManager.cpp +++ b/src/ThemeManager.cpp @@ -13,6 +13,8 @@ #include #include +#include "AppConfig.h" + namespace { QString runCommand(const QString &program, const QStringList &arguments, int timeoutMs) @@ -184,7 +186,7 @@ void ThemeManager::initialize(Mode forcedMode, bool persist) if (forcedMode != Mode::System) { setMode(forcedMode, persist); } else { - const QString saved = QSettings().value(QStringLiteral("theme/mode")).toString(); + const QString saved = AppConfig::settings().value(QStringLiteral("theme/mode")).toString(); m_mode = modeFromString(saved, nullptr); applyScheme(schemeForMode(m_mode), true); } @@ -195,7 +197,7 @@ void ThemeManager::setMode(Mode mode, bool persist) { m_mode = mode; if (persist) - QSettings().setValue(QStringLiteral("theme/mode"), modeToString(mode)); + AppConfig::settings().setValue(QStringLiteral("theme/mode"), modeToString(mode)); applyScheme(schemeForMode(m_mode), true); } @@ -282,6 +284,9 @@ QMenu::item { padding: 6px 28px 6px 22px; border-radius: 5px; } QMenu::item:selected { background-color: @highlight; color: @highlightedText; } QMenu::item:disabled { color: @disabled; } QMenu::separator { height: 1px; background-color: @border; margin: 5px 8px; } +QToolBar { background-color: @window; border: none; border-bottom: 1px solid @border; + spacing: 2px; padding: 2px 6px; } +QToolBar::separator { width: 1px; background-color: @border; margin: 4px 6px; } QStatusBar { background-color: @window; color: @placeholder; border-top: 1px solid @border; } QStatusBar::item { border: none; } QPlainTextEdit { background-color: @editorBase; color: @editorText; border: none; @@ -303,7 +308,7 @@ QPushButton { background-color: @button; color: @buttonText; border: 1px solid @ QPushButton:hover { background-color: @highlight; color: @highlightedText; } QPushButton:default { background-color: @accent; color: #ffffff; border-color: @accent; } QToolTip { background-color: @menuBackground; color: @text; border: 1px solid @border; padding: 4px; } -QToolButton { border: none; border-radius: 5px; padding: 3px 8px; color: @text; background: transparent; } +QToolButton { border: none; border-radius: 5px; padding: 2px 8px; color: @text; background: transparent; } QToolButton:hover { background-color: @hoverBackground; } QToolButton:checked { background-color: @highlight; color: @highlightedText; } QDockWidget { color: @text; } diff --git a/translations/voiletmark_en.qm b/translations/voiletmark_en.qm index 12a7f8f..544a9b9 100644 Binary files a/translations/voiletmark_en.qm and b/translations/voiletmark_en.qm differ diff --git a/translations/voiletmark_en.ts b/translations/voiletmark_en.ts index 373e5c3..8d34c97 100644 --- a/translations/voiletmark_en.ts +++ b/translations/voiletmark_en.ts @@ -1,9 +1,10 @@ - + + GitRunner - + (超时,已中止) @@ -13,464 +14,487 @@ MainWindow - - + + 新建 New - - + + 打开… Open… - - + + 保存 Save - - + + 另存为… Save As… - - + + 导出 PDF… Export PDF… - - + + 退出 Quit - - + + 撤销 Undo - - + + 重做 Redo - - + + 剪切 Cut - - + + 复制 Copy - - + + 粘贴 Paste - - + + 全选 Select All - - + + 自动换行 Word Wrap - - + + 放大 Zoom In - - + + 缩小 Zoom Out - - + + 实际大小 Actual Size - - + + 打开文件夹… Open Folder… - - - - + + + + 侧栏 Sidebar - - + + 源代码模式 Source Mode - - + + 代码块 Code Block - - + + 标题 %1 Heading %1 - + 文件夹 Folder - - + + 未打开文件夹 No folder open - - + + Git 仓库 Git repository - - + + 拉取 Pull - + git pull --ff-only git pull --ff-only - - + + 获取 Fetch - + git fetch --all --prune git fetch --all --prune - - - + + + 推送 Push - + git push git push - + 暂存 Stage - + git add -A git add -A - - - - - + + + + + 提交 Commit - + 把暂存区的内容提交 Commit the staged changes - - - + + + 文件 File - + 目录 Outline - - + 最近打开 Recent Files - - + + 编辑 Edit - - + + 常用 + Quick Access + + + + + 最近文件 + Recent Files + + + + + 最近目录 + Recent Folders + + + + 视图 View - - + + 主题 Theme - - + + 6:00–18:00 使用浅色,其余时间使用深色 Light from 6:00 to 18:00, dark the rest of the day - - + + 语言 Language - - + + 格式 Format - - + + 标题 Heading - - + + 帮助 Help - - - + + + 关于 VoiletMark About VoiletMark - - + + 显示 / 隐藏文件夹侧栏(Ctrl+B) Show / hide the folder sidebar (Ctrl+B) - - + + 源代码模式:关闭语法高亮与行号(Ctrl+Shift+M) Source mode: turn off highlighting and line numbers (Ctrl+Shift+M) - - + + 未命名 Untitled - + 打开 Markdown 文件 Open Markdown File - + Markdown 文件 (*.md *.markdown *.mdown *.mkd);;文本文件 (*.txt);;所有文件 (*) Markdown files (*.md *.markdown *.mdown *.mkd);;Text files (*.txt);;All files (*) - + 无法打开文件 Cannot Open File - + 读取「%1」失败:%2 - Could not read "%1": %2 + Could not read "%1": %2 - + 另存为 Save As - + Markdown 文件 (*.md);;文本文件 (*.txt);;所有文件 (*) Markdown files (*.md);;Text files (*.txt);;All files (*) - + 导出 PDF Export PDF - + PDF 文件 (*.pdf) PDF files (*.pdf) - + 正在导出 PDF… Exporting PDF… - - + + 导出失败 Export Failed - + 文档排版结果异常,已中止导出。 The document layout looks broken; export was aborted. - + 没能写出「%1」,换个位置再试一次。 - Could not write "%1". Try another location. + Could not write "%1". Try another location. - + 已导出 %1 Exported to %1 - - - + + + 无法保存文件 Cannot Save File - + 写入「%1」失败:%2 - Could not write "%1": %2 + Could not write "%1": %2 - + 写入「%1」时发生错误。 - An error occurred while writing "%1". + An error occurred while writing "%1". - + 提交「%1」失败:%2 - Could not commit "%1": %2 + Could not commit "%1": %2 - + 已保存 %1 Saved to %1 - + VoiletMark VoiletMark - + 「%1」有未保存的修改,是否保存? - "%1" has unsaved changes. Save them? + "%1" has unsaved changes. Save them? - + %1[*] — VoiletMark %1[*] — VoiletMark - + 行 %1,列 %2 Line %1, Col %2 - + 已选 %1 字 %1 selected - + %1 字 %1 chars - + <h3>VoiletMark %1</h3><p>一个轻量的跨平台 Markdown 编辑器。</p><p>基于 Qt %2 与 C++17 构建。</p> <h3>VoiletMark %1</h3><p>A lightweight cross-platform Markdown editor.</p><p>Built with Qt %2 and C++17.</p> - + + 配置文件:%1 + Config file: %1 + + + + (暂无记录) (none) - + + 清空列表 Clear List - + 打开文件夹 Open Folder - + 文件 → 打开文件夹… File → Open Folder… - + (没有 Markdown 文件) (no Markdown files) - + 正在%1… %1… - + Git 操作失败 Git Operation Failed - + %1失败: %2 @@ -479,124 +503,124 @@ %2 - + (没有输出) (no output) - + %1完成 %1 finished - + (没有标题) (no headings) - + 暂存区是空的,先点「暂存」把所有改动加进去。 - Nothing is staged yet. Click "Stage" first to add all changes. + Nothing is staged yet. Click "Stage" first to add all changes. - + 提交说明: Commit message: - + 添加到提交 Add to commit - + 「%1」已经在 .gitignore 里了 - "%1" is already in .gitignore + "%1" is already in .gitignore - + 忽略文件 Ignore File - + 打不开 .gitignore:%1 Cannot open .gitignore: %1 - + 已把 %1 写进 .gitignore Added %1 to .gitignore - - - + + + 删除远端 Delete from remote - + 「%1」没有被 git 跟踪,远端本来就没有它。 - "%1" is not tracked by git, so the remote never had it. + "%1" is not tracked by git, so the remote never had it. - + 从仓库删除「%1」并推送到远端? 本地文件会保留,远端和别人 pull 下来的仓库里会删掉它。 - Remove "%1" from the repository and push? + Remove "%1" from the repository and push? -Your local file is kept, but the remote copy and everyone else's will be deleted. +Your local file is kept, but the remote copy and everyone else's will be deleted. - + 删除并推送 Delete and push - + chore: 从仓库删除 %1 chore: remove %1 from repository - - + + 添加 Add - + git add 这个文件 git add this file - + 忽略 Ignore - + 把路径写进 .gitignore(不改仓库里已跟踪的文件) Add the path to .gitignore (does not untrack files already in the repo) - + git rm --cached 后提交并推送:远端删除,本地保留 git rm --cached, then commit and push: gone from the remote, kept locally - + 取消 Cancel - + 从仓库移除 Remove from repository - + 打开 Open @@ -604,40 +628,40 @@ Your local file is kept, but the remote copy and everyone else's will be deleted MarkdownEditor - - + + 已复制 Copied - - + + 复制 Copy - - + + 纯文本 Plain text - + 插入行 Insert row - + 删除行 Delete row - + 插入列 Insert column - + 删除列 Delete column @@ -645,24 +669,24 @@ Your local file is kept, but the remote copy and everyone else's will be deleted QObject - + 浅色 Light - + 深色 Dark - + 按时段自动 Scheduled - + 跟随系统 Follow System - \ No newline at end of file +