mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-04 21:39:52 +08:00
theme: support hot-reload via --watch-themes cmd option
This commit is contained in:
parent
6150d327da
commit
62f6b7f3c5
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
#include <QFileOpenEvent>
|
#include <QFileOpenEvent>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <core/vnotex.h>
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -10,6 +15,53 @@ Application::Application(int &p_argc, char **p_argv)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::watchThemeFolder(const QString &p_themeFolderPath)
|
||||||
|
{
|
||||||
|
if (p_themeFolderPath.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize watchers only when needed
|
||||||
|
if (!m_styleWatcher) {
|
||||||
|
m_styleWatcher = new QFileSystemWatcher(this);
|
||||||
|
}
|
||||||
|
if (!m_reloadTimer) {
|
||||||
|
m_reloadTimer = new QTimer(this);
|
||||||
|
m_reloadTimer->setSingleShot(true);
|
||||||
|
m_reloadTimer->setInterval(500); // 500ms debounce delay
|
||||||
|
connect(m_reloadTimer, &QTimer::timeout,
|
||||||
|
this, &Application::reloadThemeResources);
|
||||||
|
|
||||||
|
// Connect file watcher to timer
|
||||||
|
connect(m_styleWatcher, &QFileSystemWatcher::directoryChanged,
|
||||||
|
m_reloadTimer, qOverload<>(&QTimer::start));
|
||||||
|
connect(m_styleWatcher, &QFileSystemWatcher::fileChanged,
|
||||||
|
m_reloadTimer, qOverload<>(&QTimer::start));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch the theme folder and its files
|
||||||
|
m_styleWatcher->addPath(p_themeFolderPath);
|
||||||
|
|
||||||
|
// Also watch individual files in the theme folder
|
||||||
|
QDir themeDir(p_themeFolderPath);
|
||||||
|
QStringList files = themeDir.entryList(QDir::Files);
|
||||||
|
for (const QString &file : files) {
|
||||||
|
m_styleWatcher->addPath(themeDir.filePath(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::reloadThemeResources()
|
||||||
|
{
|
||||||
|
VNoteX::getInst().getThemeMgr().refreshCurrentTheme();
|
||||||
|
|
||||||
|
auto stylesheet = VNoteX::getInst().getThemeMgr().fetchQtStyleSheet();
|
||||||
|
if (!stylesheet.isEmpty()) {
|
||||||
|
setStyleSheet(stylesheet);
|
||||||
|
style()->unpolish(this);
|
||||||
|
style()->polish(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Application::event(QEvent *p_event)
|
bool Application::event(QEvent *p_event)
|
||||||
{
|
{
|
||||||
// On macOS, we need this to open file from Finder.
|
// On macOS, we need this to open file from Finder.
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#ifndef APPLICATION_H
|
#ifndef APPLICATION_H
|
||||||
#define APPLICATION_H
|
#define APPLICATION_H
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
||||||
|
class QFileSystemWatcher;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
namespace vnotex
|
namespace vnotex
|
||||||
{
|
{
|
||||||
class Application : public QApplication
|
class Application : public QApplication
|
||||||
@ -11,11 +13,21 @@ namespace vnotex
|
|||||||
public:
|
public:
|
||||||
Application(int &p_argc, char **p_argv);
|
Application(int &p_argc, char **p_argv);
|
||||||
|
|
||||||
|
// Set up theme folder watcher for hot-reload
|
||||||
|
void watchThemeFolder(const QString &p_themeFolderPath);
|
||||||
|
|
||||||
|
// Reload the theme resources (stylesheet, icons, etc)
|
||||||
|
void reloadThemeResources();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void openFileRequested(const QString &p_filePath);
|
void openFileRequested(const QString &p_filePath);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool event(QEvent *p_event) Q_DECL_OVERRIDE;
|
bool event(QEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFileSystemWatcher *m_styleWatcher = nullptr;
|
||||||
|
QTimer *m_reloadTimer = nullptr;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a
|
|||||||
const QCommandLineOption logStderrOpt("log-stderr", MainWindow::tr("Log to stderr."));
|
const QCommandLineOption logStderrOpt("log-stderr", MainWindow::tr("Log to stderr."));
|
||||||
parser.addOption(logStderrOpt);
|
parser.addOption(logStderrOpt);
|
||||||
|
|
||||||
|
const QCommandLineOption watchThemesOpt("watch-themes", MainWindow::tr("Watch theme folder for changes."));
|
||||||
|
parser.addOption(watchThemesOpt);
|
||||||
|
|
||||||
// WebEngine options.
|
// WebEngine options.
|
||||||
// No need to handle them. Just add them to the parser to avoid parse error.
|
// No need to handle them. Just add them to the parser to avoid parse error.
|
||||||
{
|
{
|
||||||
@ -70,5 +73,9 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a
|
|||||||
m_logToStderr = true;
|
m_logToStderr = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(watchThemesOpt)) {
|
||||||
|
m_watchThemes = true;
|
||||||
|
}
|
||||||
|
|
||||||
return ParseResult::Ok;
|
return ParseResult::Ok;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,9 @@ public:
|
|||||||
bool m_verbose = false;
|
bool m_verbose = false;
|
||||||
|
|
||||||
bool m_logToStderr = false;
|
bool m_logToStderr = false;
|
||||||
|
|
||||||
|
// Whether to watch theme folder for changes
|
||||||
|
bool m_watchThemes = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COMMANDLINEOPTIONS_H
|
#endif // COMMANDLINEOPTIONS_H
|
||||||
|
@ -24,6 +24,11 @@ Theme::Theme(const QString &p_themeFolderPath,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString vnotex::Theme::getThemeFolder() const
|
||||||
|
{
|
||||||
|
return m_themeFolderPath;
|
||||||
|
}
|
||||||
|
|
||||||
bool Theme::isValidThemeFolder(const QString &p_folder)
|
bool Theme::isValidThemeFolder(const QString &p_folder)
|
||||||
{
|
{
|
||||||
QDir dir(p_folder);
|
QDir dir(p_folder);
|
||||||
|
@ -47,6 +47,8 @@ namespace vnotex
|
|||||||
|
|
||||||
QString name() const;
|
QString name() const;
|
||||||
|
|
||||||
|
QString getThemeFolder() const;
|
||||||
|
|
||||||
static bool isValidThemeFolder(const QString &p_folder);
|
static bool isValidThemeFolder(const QString &p_folder);
|
||||||
|
|
||||||
static Theme *fromFolder(const QString &p_folder);
|
static Theme *fromFolder(const QString &p_folder);
|
||||||
|
@ -24,8 +24,6 @@ ThemeMgr::ThemeMgr(const QString &p_currentThemeName, QObject *p_parent)
|
|||||||
loadAvailableThemes();
|
loadAvailableThemes();
|
||||||
|
|
||||||
loadCurrentTheme(p_currentThemeName);
|
loadCurrentTheme(p_currentThemeName);
|
||||||
|
|
||||||
IconUtils::setDefaultIconForeground(paletteColor("base#icon#fg"), paletteColor("base#icon#disabled#fg"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ThemeMgr::getIconFile(const QString &p_icon) const
|
QString ThemeMgr::getIconFile(const QString &p_icon) const
|
||||||
@ -91,6 +89,7 @@ const Theme &ThemeMgr::getCurrentTheme() const
|
|||||||
|
|
||||||
void ThemeMgr::loadCurrentTheme(const QString &p_themeName)
|
void ThemeMgr::loadCurrentTheme(const QString &p_themeName)
|
||||||
{
|
{
|
||||||
|
m_currentTheme.reset();
|
||||||
auto themeFolder = findThemeFolder(p_themeName);
|
auto themeFolder = findThemeFolder(p_themeName);
|
||||||
if (themeFolder.isNull()) {
|
if (themeFolder.isNull()) {
|
||||||
qWarning() << "failed to locate theme" << p_themeName;
|
qWarning() << "failed to locate theme" << p_themeName;
|
||||||
@ -104,6 +103,8 @@ void ThemeMgr::loadCurrentTheme(const QString &p_themeName)
|
|||||||
qWarning() << "fall back to default theme" << defaultTheme;
|
qWarning() << "fall back to default theme" << defaultTheme;
|
||||||
m_currentTheme.reset(loadTheme(findThemeFolder(defaultTheme)));
|
m_currentTheme.reset(loadTheme(findThemeFolder(defaultTheme)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IconUtils::setDefaultIconForeground(paletteColor("base#icon#fg"), paletteColor("base#icon#disabled#fg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Theme *ThemeMgr::loadTheme(const QString &p_themeFolder)
|
Theme *ThemeMgr::loadTheme(const QString &p_themeFolder)
|
||||||
@ -211,6 +212,14 @@ QPixmap ThemeMgr::getThemePreview(const QString &p_name) const
|
|||||||
void ThemeMgr::refresh()
|
void ThemeMgr::refresh()
|
||||||
{
|
{
|
||||||
loadAvailableThemes();
|
loadAvailableThemes();
|
||||||
|
refreshCurrentTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
void vnotex::ThemeMgr::refreshCurrentTheme()
|
||||||
|
{
|
||||||
|
if (m_currentTheme) {
|
||||||
|
loadCurrentTheme(m_currentTheme->name());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThemeMgr::addWebStylesSearchPath(const QString &p_path)
|
void ThemeMgr::addWebStylesSearchPath(const QString &p_path)
|
||||||
|
@ -60,10 +60,11 @@ namespace vnotex
|
|||||||
|
|
||||||
const ThemeInfo *findTheme(const QString &p_name) const;
|
const ThemeInfo *findTheme(const QString &p_name) const;
|
||||||
|
|
||||||
// Refresh the themes list.
|
// Refresh the themes list and reload current theme.
|
||||||
// Won't affect current theme since we do not support changing theme real time for now.
|
|
||||||
void refresh();
|
void refresh();
|
||||||
|
|
||||||
|
void refreshCurrentTheme();
|
||||||
|
|
||||||
// Return all web stylesheets available, including those from themes and web styles search paths.
|
// Return all web stylesheets available, including those from themes and web styles search paths.
|
||||||
// <DisplayName, FilePath>.
|
// <DisplayName, FilePath>.
|
||||||
QVector<QPair<QString, QString>> getWebStyles() const;
|
QVector<QPair<QString, QString>> getWebStyles() const;
|
||||||
|
@ -161,6 +161,11 @@ int main(int argc, char *argv[])
|
|||||||
auto style = VNoteX::getInst().getThemeMgr().fetchQtStyleSheet();
|
auto style = VNoteX::getInst().getThemeMgr().fetchQtStyleSheet();
|
||||||
if (!style.isEmpty()) {
|
if (!style.isEmpty()) {
|
||||||
app.setStyleSheet(style);
|
app.setStyleSheet(style);
|
||||||
|
// Set up hot-reload for the theme folder if enabled via command line
|
||||||
|
if (cmdOptions.m_watchThemes) {
|
||||||
|
const auto themeFolderPath = VNoteX::getInst().getThemeMgr().getCurrentTheme().getThemeFolder();
|
||||||
|
app.watchThemeFolder(themeFolderPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user