Skip to content

Commit 792fba8

Browse files
authored
Merge pull request #1 from broccoli-97/dev
2 parents 3a0f6a5 + 38d897f commit 792fba8

22 files changed

Lines changed: 1154 additions & 143 deletions

.github/workflows/ci.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ on:
1414
- 'LICENSE'
1515
- '.gitignore'
1616

17+
env:
18+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
19+
1720
jobs:
1821
build:
1922
strategy:
@@ -31,7 +34,7 @@ jobs:
3134
name: Build (${{ matrix.name }})
3235

3336
steps:
34-
- uses: actions/checkout@v4
37+
- uses: actions/checkout@v5
3538

3639
- name: Install dependencies (Linux)
3740
if: runner.os == 'Linux'

.github/workflows/msstore.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ on:
1212
permissions:
1313
contents: write
1414

15+
env:
16+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
17+
1518
jobs:
1619
msix:
1720
runs-on: windows-latest
1821
name: Build MSIX
1922

2023
steps:
21-
- uses: actions/checkout@v4
24+
- uses: actions/checkout@v5
2225

2326
- name: Install Qt6
2427
uses: jurplel/install-qt-action@v4
@@ -44,7 +47,7 @@ jobs:
4447
echo "Version: ${VER}, MSIX: ${MSIX_VER}"
4548
4649
- name: Configure
47-
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
50+
run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DAPP_VERSION=${{ steps.version.outputs.version }}
4851

4952
- name: Build
5053
run: cmake --build build --config Release

.github/workflows/release.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ on:
77
permissions:
88
contents: write
99

10+
env:
11+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
12+
1013
jobs:
1114
build:
1215
strategy:
@@ -24,7 +27,14 @@ jobs:
2427
name: Build (${{ matrix.name }})
2528

2629
steps:
27-
- uses: actions/checkout@v4
30+
- uses: actions/checkout@v5
31+
32+
- name: Determine version
33+
id: version
34+
shell: bash
35+
run: |
36+
VER="${GITHUB_REF#refs/tags/v}"
37+
echo "version=${VER}" >> "$GITHUB_OUTPUT"
2838
2939
- name: Install dependencies (Linux)
3040
if: runner.os == 'Linux'
@@ -36,7 +46,7 @@ jobs:
3646
version: '6.7.*'
3747

3848
- name: Configure
39-
run: cmake -B build -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release
49+
run: cmake -B build -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DAPP_VERSION=${{ steps.version.outputs.version }}
4050

4151
- name: Build
4252
run: cmake --build build --config Release

CMakeLists.txt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ endif()
2626

2727
# ---- Dependencies ------------------------------------------------------------
2828

29-
find_package(Qt6 REQUIRED COMPONENTS Widgets Svg PrintSupport)
29+
find_package(Qt6 REQUIRED COMPONENTS Widgets Svg PrintSupport Network LinguistTools)
3030

3131
# ---- Library (all sources except main.cpp) -----------------------------------
3232

@@ -40,6 +40,7 @@ add_library(ymind_lib OBJECT
4040
src/core/SettingsDialog.h src/core/SettingsDialog.cpp
4141
src/core/TemplateDescriptor.h src/core/TemplateDescriptor.cpp
4242
src/core/TemplateRegistry.h src/core/TemplateRegistry.cpp
43+
src/core/UpdateChecker.h src/core/UpdateChecker.cpp
4344

4445
# Scene – graphics-scene items
4546
src/scene/EdgeItem.h src/scene/EdgeItem.cpp
@@ -68,12 +69,25 @@ add_library(ymind_lib OBJECT
6869

6970
target_include_directories(ymind_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
7071

71-
target_compile_definitions(ymind_lib PUBLIC YMIND_VERSION="${PROJECT_VERSION}")
72+
set(APP_VERSION "${PROJECT_VERSION}" CACHE STRING "Application version string")
73+
target_compile_definitions(ymind_lib PUBLIC YMIND_VERSION="${APP_VERSION}")
7274

7375
target_link_libraries(ymind_lib PUBLIC
7476
Qt6::Widgets
7577
Qt6::Svg
7678
Qt6::PrintSupport
79+
Qt6::Network
80+
)
81+
82+
# ---- Translations ------------------------------------------------------------
83+
84+
set(TS_FILES
85+
translations/ymind_zh_CN.ts
86+
)
87+
88+
qt_add_translations(ymind_lib
89+
TS_FILES ${TS_FILES}
90+
RESOURCE_PREFIX "/translations"
7791
)
7892

7993
# ---- Executable --------------------------------------------------------------

src/core/AboutDialog.cpp

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <QVBoxLayout>
88

99
AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent) {
10-
setWindowTitle("About YMind");
10+
setWindowTitle(tr("About YMind"));
1111
setFixedWidth(420);
1212

1313
QString linkColor = ThemeManager::isDark() ? "#5cacee" : "#0563C1";
@@ -26,18 +26,20 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent) {
2626
appLabel->setWordWrap(true);
2727
appLabel->setText(
2828
QString("<h2>YMind</h2>"
29-
"<p>Version %1</p>"
30-
"<p>A desktop mind map editor.</p>")
31-
.arg(QCoreApplication::applicationVersion()));
29+
"<p>%1</p>"
30+
"<p>%2</p>")
31+
.arg(tr("Version %1").arg(QCoreApplication::applicationVersion()),
32+
tr("A desktop mind map editor.")));
3233
layout->addWidget(appLabel);
3334

3435
// License
3536
auto* licenseLabel = new QLabel(this);
3637
licenseLabel->setTextFormat(Qt::RichText);
3738
licenseLabel->setWordWrap(true);
38-
licenseLabel->setText(QString("<p>Licensed under the %1.</p>")
39-
.arg(makeLink("https://www.apache.org/licenses/LICENSE-2.0",
40-
"Apache License 2.0")));
39+
licenseLabel->setText(QString("<p>%1</p>")
40+
.arg(tr("Licensed under the %1.")
41+
.arg(makeLink("https://www.apache.org/licenses/LICENSE-2.0",
42+
"Apache License 2.0"))));
4143
licenseLabel->setOpenExternalLinks(true);
4244
layout->addWidget(licenseLabel);
4345

@@ -47,18 +49,19 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent) {
4749
qtLabel->setWordWrap(true);
4850
QString qtOpenSource = "https://www.qt.io/download/open-source";
4951
qtLabel->setText(
50-
QString("<p>This application uses Qt %1, licensed under the %2. "
51-
"Qt source code and re-linking instructions are available at: %3.</p>")
52-
.arg(qVersion(),
53-
makeLink("https://www.gnu.org/licenses/lgpl-3.0.html", "GNU LGPL v3"),
54-
makeLink(qtOpenSource, qtOpenSource)));
52+
QString("<p>%1</p>")
53+
.arg(tr("This application uses Qt %1, licensed under the %2. "
54+
"Qt source code and re-linking instructions are available at: %3.")
55+
.arg(qVersion(),
56+
makeLink("https://www.gnu.org/licenses/lgpl-3.0.html", "GNU LGPL v3"),
57+
makeLink(qtOpenSource, qtOpenSource))));
5558
qtLabel->setOpenExternalLinks(true);
5659
layout->addWidget(qtLabel);
5760

5861
layout->addStretch();
5962

6063
// Close button
61-
auto* closeBtn = new QPushButton("Close", this);
64+
auto* closeBtn = new QPushButton(tr("Close"), this);
6265
closeBtn->setDefault(true);
6366
connect(closeBtn, &QPushButton::clicked, this, &QDialog::accept);
6467

src/core/AppSettings.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,19 @@ QByteArray AppSettings::windowState() const {
9292
void AppSettings::setWindowState(const QByteArray& state) {
9393
m_settings->setValue("window/state", state);
9494
}
95+
96+
bool AppSettings::checkForUpdatesEnabled() const {
97+
return m_settings->value("updates/checkOnStartup", false).toBool();
98+
}
99+
100+
void AppSettings::setCheckForUpdatesEnabled(bool enabled) {
101+
m_settings->setValue("updates/checkOnStartup", enabled);
102+
}
103+
104+
QString AppSettings::language() const {
105+
return m_settings->value("appearance/language", "en").toString();
106+
}
107+
108+
void AppSettings::setLanguage(const QString& lang) {
109+
m_settings->setValue("appearance/language", lang);
110+
}

src/core/AppSettings.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ class AppSettings : public QObject {
3535
QByteArray windowState() const;
3636
void setWindowState(const QByteArray& state);
3737

38+
bool checkForUpdatesEnabled() const;
39+
void setCheckForUpdatesEnabled(bool enabled);
40+
41+
QString language() const;
42+
void setLanguage(const QString& lang);
43+
3844
signals:
3945
void themeChanged(AppTheme theme);
4046
void autoSaveSettingsChanged();

src/core/FileManager.cpp

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ void FileManager::newFile() {
2323

2424
void FileManager::openFile() {
2525
QString filePath =
26-
QFileDialog::getOpenFileName(m_window, "Open Mind Map", QString(),
27-
"YMind Files (*.ymind);;JSON Files (*.json);;All Files (*)");
26+
QFileDialog::getOpenFileName(m_window, tr("Open Mind Map"), QString(),
27+
tr("YMind Files (*.ymind);;JSON Files (*.json);;All Files (*)"));
2828
if (filePath.isEmpty())
2929
return;
3030

@@ -39,7 +39,7 @@ void FileManager::openFile() {
3939
auto* scene = m_tabManager->currentScene();
4040
auto* view = m_tabManager->currentView();
4141
if (!scene->loadFromFile(filePath)) {
42-
QMessageBox::warning(m_window, "YMind", "Could not open file:\n" + filePath);
42+
QMessageBox::warning(m_window, "YMind", tr("Could not open file:\n%1").arg(filePath));
4343
return;
4444
}
4545
m_tabManager->setCurrentFilePath(filePath);
@@ -55,7 +55,7 @@ void FileManager::openFile() {
5555
view->setScene(scene);
5656

5757
if (!scene->loadFromFile(filePath)) {
58-
QMessageBox::warning(m_window, "YMind", "Could not open file:\n" + filePath);
58+
QMessageBox::warning(m_window, "YMind", tr("Could not open file:\n%1").arg(filePath));
5959
delete scene;
6060
delete view;
6161
return;
@@ -80,7 +80,7 @@ void FileManager::saveFile() {
8080
QString path = m_tabManager->currentFilePath();
8181

8282
if (!scene->saveToFile(path)) {
83-
QMessageBox::warning(m_window, "YMind", "Could not save file:\n" + path);
83+
QMessageBox::warning(m_window, "YMind", tr("Could not save file:\n%1").arg(path));
8484
}
8585

8686
int cur = m_tabManager->currentIndex();
@@ -92,8 +92,8 @@ void FileManager::saveFile() {
9292

9393
void FileManager::saveFileAs() {
9494
QString filePath =
95-
QFileDialog::getSaveFileName(m_window, "Save Mind Map", QString(),
96-
"YMind Files (*.ymind);;JSON Files (*.json);;All Files (*)");
95+
QFileDialog::getSaveFileName(m_window, tr("Save Mind Map"), QString(),
96+
tr("YMind Files (*.ymind);;JSON Files (*.json);;All Files (*)"));
9797
if (filePath.isEmpty())
9898
return;
9999

@@ -102,7 +102,7 @@ void FileManager::saveFileAs() {
102102

103103
auto* scene = m_tabManager->currentScene();
104104
if (!scene->saveToFile(filePath)) {
105-
QMessageBox::warning(m_window, "YMind", "Could not save file:\n" + filePath);
105+
QMessageBox::warning(m_window, "YMind", tr("Could not save file:\n%1").arg(filePath));
106106
return;
107107
}
108108

@@ -130,15 +130,15 @@ void FileManager::doExport(const QString& dialogTitle, const QString& filter,
130130

131131
if (!exporter(filePath)) {
132132
QMessageBox::warning(m_window, "YMind",
133-
QString("Could not export %1:\n%2").arg(errorLabel, filePath));
133+
tr("Could not export %1:\n%2").arg(errorLabel, filePath));
134134
return;
135135
}
136136
if (auto* mw = qobject_cast<QMainWindow*>(m_window))
137-
mw->statusBar()->showMessage("Exported to " + filePath, 3000);
137+
mw->statusBar()->showMessage(tr("Exported to %1").arg(filePath), 3000);
138138
}
139139

140140
void FileManager::exportAsText() {
141-
doExport("Export as Text", "Text Files (*.txt);;All Files (*)", ".txt",
141+
doExport(tr("Export as Text"), tr("Text Files (*.txt);;All Files (*)"), ".txt",
142142
[this](const QString& path) {
143143
QFile file(path);
144144
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
@@ -147,11 +147,11 @@ void FileManager::exportAsText() {
147147
file.close();
148148
return true;
149149
},
150-
"file");
150+
tr("file"));
151151
}
152152

153153
void FileManager::exportAsMarkdown() {
154-
doExport("Export as Markdown", "Markdown Files (*.md);;All Files (*)", ".md",
154+
doExport(tr("Export as Markdown"), tr("Markdown Files (*.md);;All Files (*)"), ".md",
155155
[this](const QString& path) {
156156
QFile file(path);
157157
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
@@ -160,42 +160,42 @@ void FileManager::exportAsMarkdown() {
160160
file.close();
161161
return true;
162162
},
163-
"file");
163+
tr("file"));
164164
}
165165

166166
void FileManager::exportAsPng() {
167-
doExport("Export as PNG", "PNG Images (*.png);;All Files (*)", ".png",
167+
doExport(tr("Export as PNG"), tr("PNG Images (*.png);;All Files (*)"), ".png",
168168
[this](const QString& path) {
169169
return m_tabManager->currentScene()->exportToPng(path);
170170
},
171171
"PNG");
172172
}
173173

174174
void FileManager::exportAsSvg() {
175-
doExport("Export as SVG", "SVG Files (*.svg);;All Files (*)", ".svg",
175+
doExport(tr("Export as SVG"), tr("SVG Files (*.svg);;All Files (*)"), ".svg",
176176
[this](const QString& path) {
177177
return m_tabManager->currentScene()->exportToSvg(path);
178178
},
179179
"SVG");
180180
}
181181

182182
void FileManager::exportAsPdf() {
183-
doExport("Export as PDF", "PDF Files (*.pdf);;All Files (*)", ".pdf",
183+
doExport(tr("Export as PDF"), tr("PDF Files (*.pdf);;All Files (*)"), ".pdf",
184184
[this](const QString& path) {
185185
return m_tabManager->currentScene()->exportToPdf(path);
186186
},
187187
"PDF");
188188
}
189189

190190
void FileManager::importFromText() {
191-
QString filePath = QFileDialog::getOpenFileName(m_window, "Import from Text", QString(),
192-
"Text Files (*.txt);;All Files (*)");
191+
QString filePath = QFileDialog::getOpenFileName(m_window, tr("Import from Text"), QString(),
192+
tr("Text Files (*.txt);;All Files (*)"));
193193
if (filePath.isEmpty())
194194
return;
195195

196196
QFile file(filePath);
197197
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
198-
QMessageBox::warning(m_window, "YMind", "Could not read file:\n" + filePath);
198+
QMessageBox::warning(m_window, "YMind", tr("Could not read file:\n%1").arg(filePath));
199199
return;
200200
}
201201
QString text = QString::fromUtf8(file.readAll());
@@ -206,7 +206,8 @@ void FileManager::importFromText() {
206206
auto* scene = m_tabManager->currentScene();
207207
auto* view = m_tabManager->currentView();
208208
if (!scene->importFromText(text)) {
209-
QMessageBox::warning(m_window, "YMind", "Could not parse text file:\n" + filePath);
209+
QMessageBox::warning(m_window, "YMind",
210+
tr("Could not parse text file:\n%1").arg(filePath));
210211
return;
211212
}
212213
m_tabManager->setCurrentFilePath(QString());
@@ -221,7 +222,8 @@ void FileManager::importFromText() {
221222
view->setScene(scene);
222223

223224
if (!scene->importFromText(text)) {
224-
QMessageBox::warning(m_window, "YMind", "Could not parse text file:\n" + filePath);
225+
QMessageBox::warning(m_window, "YMind",
226+
tr("Could not parse text file:\n%1").arg(filePath));
225227
delete scene;
226228
delete view;
227229
return;
@@ -236,5 +238,5 @@ void FileManager::importFromText() {
236238
}
237239

238240
if (auto* mw = qobject_cast<QMainWindow*>(m_window))
239-
mw->statusBar()->showMessage("Imported from " + filePath, 3000);
241+
mw->statusBar()->showMessage(tr("Imported from %1").arg(filePath), 3000);
240242
}

0 commit comments

Comments
 (0)