Skip to content

Commit 62f6b7f

Browse files
committed
theme: support hot-reload via --watch-themes cmd option
1 parent 6150d32 commit 62f6b7f

File tree

9 files changed

+101
-5
lines changed

9 files changed

+101
-5
lines changed

src/application.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
#include <QFileOpenEvent>
44
#include <QDebug>
5+
#include <QDir>
6+
#include <QStyle>
7+
#include <QFileSystemWatcher>
8+
#include <QTimer>
9+
#include <core/vnotex.h>
510

611
using namespace vnotex;
712

@@ -10,6 +15,53 @@ Application::Application(int &p_argc, char **p_argv)
1015
{
1116
}
1217

18+
void Application::watchThemeFolder(const QString &p_themeFolderPath)
19+
{
20+
if (p_themeFolderPath.isEmpty()) {
21+
return;
22+
}
23+
24+
// Initialize watchers only when needed
25+
if (!m_styleWatcher) {
26+
m_styleWatcher = new QFileSystemWatcher(this);
27+
}
28+
if (!m_reloadTimer) {
29+
m_reloadTimer = new QTimer(this);
30+
m_reloadTimer->setSingleShot(true);
31+
m_reloadTimer->setInterval(500); // 500ms debounce delay
32+
connect(m_reloadTimer, &QTimer::timeout,
33+
this, &Application::reloadThemeResources);
34+
35+
// Connect file watcher to timer
36+
connect(m_styleWatcher, &QFileSystemWatcher::directoryChanged,
37+
m_reloadTimer, qOverload<>(&QTimer::start));
38+
connect(m_styleWatcher, &QFileSystemWatcher::fileChanged,
39+
m_reloadTimer, qOverload<>(&QTimer::start));
40+
}
41+
42+
// Watch the theme folder and its files
43+
m_styleWatcher->addPath(p_themeFolderPath);
44+
45+
// Also watch individual files in the theme folder
46+
QDir themeDir(p_themeFolderPath);
47+
QStringList files = themeDir.entryList(QDir::Files);
48+
for (const QString &file : files) {
49+
m_styleWatcher->addPath(themeDir.filePath(file));
50+
}
51+
}
52+
53+
void Application::reloadThemeResources()
54+
{
55+
VNoteX::getInst().getThemeMgr().refreshCurrentTheme();
56+
57+
auto stylesheet = VNoteX::getInst().getThemeMgr().fetchQtStyleSheet();
58+
if (!stylesheet.isEmpty()) {
59+
setStyleSheet(stylesheet);
60+
style()->unpolish(this);
61+
style()->polish(this);
62+
}
63+
}
64+
1365
bool Application::event(QEvent *p_event)
1466
{
1567
// On macOS, we need this to open file from Finder.

src/application.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#ifndef APPLICATION_H
22
#define APPLICATION_H
3-
43
#include <QApplication>
54

5+
class QFileSystemWatcher;
6+
class QTimer;
7+
68
namespace vnotex
79
{
810
class Application : public QApplication
@@ -11,11 +13,21 @@ namespace vnotex
1113
public:
1214
Application(int &p_argc, char **p_argv);
1315

16+
// Set up theme folder watcher for hot-reload
17+
void watchThemeFolder(const QString &p_themeFolderPath);
18+
19+
// Reload the theme resources (stylesheet, icons, etc)
20+
void reloadThemeResources();
21+
1422
signals:
1523
void openFileRequested(const QString &p_filePath);
1624

1725
protected:
1826
bool event(QEvent *p_event) Q_DECL_OVERRIDE;
27+
28+
private:
29+
QFileSystemWatcher *m_styleWatcher = nullptr;
30+
QTimer *m_reloadTimer = nullptr;
1931
};
2032
}
2133

src/commandlineoptions.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a
2525
const QCommandLineOption logStderrOpt("log-stderr", MainWindow::tr("Log to stderr."));
2626
parser.addOption(logStderrOpt);
2727

28+
const QCommandLineOption watchThemesOpt("watch-themes", MainWindow::tr("Watch theme folder for changes."));
29+
parser.addOption(watchThemesOpt);
30+
2831
// WebEngine options.
2932
// No need to handle them. Just add them to the parser to avoid parse error.
3033
{
@@ -70,5 +73,9 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a
7073
m_logToStderr = true;
7174
}
7275

76+
if (parser.isSet(watchThemesOpt)) {
77+
m_watchThemes = true;
78+
}
79+
7380
return ParseResult::Ok;
7481
}

src/commandlineoptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class CommandLineOptions
2727
bool m_verbose = false;
2828

2929
bool m_logToStderr = false;
30+
31+
// Whether to watch theme folder for changes
32+
bool m_watchThemes = false;
3033
};
3134

3235
#endif // COMMANDLINEOPTIONS_H

src/core/theme.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ Theme::Theme(const QString &p_themeFolderPath,
2424
{
2525
}
2626

27+
QString vnotex::Theme::getThemeFolder() const
28+
{
29+
return m_themeFolderPath;
30+
}
31+
2732
bool Theme::isValidThemeFolder(const QString &p_folder)
2833
{
2934
QDir dir(p_folder);

src/core/theme.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ namespace vnotex
4747

4848
QString name() const;
4949

50+
QString getThemeFolder() const;
51+
5052
static bool isValidThemeFolder(const QString &p_folder);
5153

5254
static Theme *fromFolder(const QString &p_folder);

src/core/thememgr.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ ThemeMgr::ThemeMgr(const QString &p_currentThemeName, QObject *p_parent)
2424
loadAvailableThemes();
2525

2626
loadCurrentTheme(p_currentThemeName);
27-
28-
IconUtils::setDefaultIconForeground(paletteColor("base#icon#fg"), paletteColor("base#icon#disabled#fg"));
2927
}
3028

3129
QString ThemeMgr::getIconFile(const QString &p_icon) const
@@ -91,6 +89,7 @@ const Theme &ThemeMgr::getCurrentTheme() const
9189

9290
void ThemeMgr::loadCurrentTheme(const QString &p_themeName)
9391
{
92+
m_currentTheme.reset();
9493
auto themeFolder = findThemeFolder(p_themeName);
9594
if (themeFolder.isNull()) {
9695
qWarning() << "failed to locate theme" << p_themeName;
@@ -104,6 +103,8 @@ void ThemeMgr::loadCurrentTheme(const QString &p_themeName)
104103
qWarning() << "fall back to default theme" << defaultTheme;
105104
m_currentTheme.reset(loadTheme(findThemeFolder(defaultTheme)));
106105
}
106+
107+
IconUtils::setDefaultIconForeground(paletteColor("base#icon#fg"), paletteColor("base#icon#disabled#fg"));
107108
}
108109

109110
Theme *ThemeMgr::loadTheme(const QString &p_themeFolder)
@@ -211,6 +212,14 @@ QPixmap ThemeMgr::getThemePreview(const QString &p_name) const
211212
void ThemeMgr::refresh()
212213
{
213214
loadAvailableThemes();
215+
refreshCurrentTheme();
216+
}
217+
218+
void vnotex::ThemeMgr::refreshCurrentTheme()
219+
{
220+
if (m_currentTheme) {
221+
loadCurrentTheme(m_currentTheme->name());
222+
}
214223
}
215224

216225
void ThemeMgr::addWebStylesSearchPath(const QString &p_path)

src/core/thememgr.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ namespace vnotex
6060

6161
const ThemeInfo *findTheme(const QString &p_name) const;
6262

63-
// Refresh the themes list.
64-
// Won't affect current theme since we do not support changing theme real time for now.
63+
// Refresh the themes list and reload current theme.
6564
void refresh();
6665

66+
void refreshCurrentTheme();
67+
6768
// Return all web stylesheets available, including those from themes and web styles search paths.
6869
// <DisplayName, FilePath>.
6970
QVector<QPair<QString, QString>> getWebStyles() const;

src/main.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ int main(int argc, char *argv[])
161161
auto style = VNoteX::getInst().getThemeMgr().fetchQtStyleSheet();
162162
if (!style.isEmpty()) {
163163
app.setStyleSheet(style);
164+
// Set up hot-reload for the theme folder if enabled via command line
165+
if (cmdOptions.m_watchThemes) {
166+
const auto themeFolderPath = VNoteX::getInst().getThemeMgr().getCurrentTheme().getThemeFolder();
167+
app.watchThemeFolder(themeFolderPath);
168+
}
164169
}
165170
}
166171

0 commit comments

Comments
 (0)