forked from quickshell/quickshell
feat: add filesystem watcher and rename QtShell object
This commit is contained in:
parent
cab5ffc65e
commit
c0d6e63f6c
|
@ -37,6 +37,7 @@ qt_add_executable(qtshell
|
||||||
src/cpp/rootwrapper.cpp
|
src/cpp/rootwrapper.cpp
|
||||||
src/cpp/qmlglobal.cpp
|
src/cpp/qmlglobal.cpp
|
||||||
src/cpp/qmlscreen.cpp
|
src/cpp/qmlscreen.cpp
|
||||||
|
src/cpp/watcher.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(qtshell URI QtShell)
|
qt_add_qml_module(qtshell URI QtShell)
|
||||||
|
|
|
@ -13,7 +13,7 @@ class QtShellGlobal: public QObject {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
Q_PROPERTY(QQmlListProperty<QtShellScreenInfo> screens READ screens NOTIFY screensChanged);
|
Q_PROPERTY(QQmlListProperty<QtShellScreenInfo> screens READ screens NOTIFY screensChanged);
|
||||||
QML_SINGLETON;
|
QML_SINGLETON;
|
||||||
QML_ELEMENT;
|
QML_NAMED_ELEMENT(QtShell);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QtShellGlobal(QObject* parent = nullptr);
|
QtShellGlobal(QObject* parent = nullptr);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include <qfileinfo.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlcomponent.h>
|
#include <qqmlcomponent.h>
|
||||||
|
@ -10,23 +11,26 @@
|
||||||
|
|
||||||
#include "scavenge.hpp"
|
#include "scavenge.hpp"
|
||||||
#include "shell.hpp"
|
#include "shell.hpp"
|
||||||
|
#include "watcher.hpp"
|
||||||
|
|
||||||
RootWrapper::RootWrapper(QUrl rootUrl):
|
RootWrapper::RootWrapper(QString rootPath):
|
||||||
QObject(nullptr), rootUrl(std::move(rootUrl)), engine(this) {
|
QObject(nullptr), rootPath(std::move(rootPath)), engine(this) {
|
||||||
this->reloadGraph(true);
|
this->reloadGraph(true);
|
||||||
|
|
||||||
if (this->activeRoot == nullptr) {
|
if (this->root == nullptr) {
|
||||||
qCritical() << "could not create scene graph, exiting";
|
qCritical() << "could not create scene graph, exiting";
|
||||||
exit(-1); // NOLINT
|
exit(-1); // NOLINT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QObject* RootWrapper::scavengeTargetFor(QObject* /* child */) { return this->root; }
|
||||||
|
|
||||||
void RootWrapper::reloadGraph(bool hard) {
|
void RootWrapper::reloadGraph(bool hard) {
|
||||||
if (this->activeRoot != nullptr) {
|
if (this->root != nullptr) {
|
||||||
this->engine.clearComponentCache();
|
this->engine.clearComponentCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto component = QQmlComponent(&this->engine, this->rootUrl);
|
auto component = QQmlComponent(&this->engine, QUrl::fromLocalFile(this->rootPath));
|
||||||
|
|
||||||
SCAVENGE_PARENT = hard ? nullptr : this;
|
SCAVENGE_PARENT = hard ? nullptr : this;
|
||||||
auto* obj = component.beginCreate(this->engine.rootContext());
|
auto* obj = component.beginCreate(this->engine.rootContext());
|
||||||
|
@ -38,7 +42,7 @@ void RootWrapper::reloadGraph(bool hard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* newRoot = qobject_cast<QtShell*>(obj);
|
auto* newRoot = qobject_cast<ShellRoot*>(obj);
|
||||||
if (newRoot == nullptr) {
|
if (newRoot == nullptr) {
|
||||||
qWarning() << "root component was not a QtShell";
|
qWarning() << "root component was not a QtShell";
|
||||||
delete obj;
|
delete obj;
|
||||||
|
@ -47,26 +51,34 @@ void RootWrapper::reloadGraph(bool hard) {
|
||||||
|
|
||||||
component.completeCreate();
|
component.completeCreate();
|
||||||
|
|
||||||
if (this->activeRoot != nullptr) {
|
if (this->root != nullptr) {
|
||||||
this->activeRoot->deleteLater();
|
this->root->deleteLater();
|
||||||
this->activeRoot = nullptr;
|
this->root = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->activeRoot = newRoot;
|
this->root = newRoot;
|
||||||
|
this->onConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWrapper::changeRoot(QtShell* newRoot) {
|
void RootWrapper::onConfigChanged() {
|
||||||
if (this->activeRoot != nullptr) {
|
auto config = this->root->config();
|
||||||
QObject::disconnect(this->destroyConnection);
|
|
||||||
this->activeRoot->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newRoot != nullptr) {
|
if (config.mWatchFiles && this->configWatcher == nullptr) {
|
||||||
this->activeRoot = newRoot;
|
this->configWatcher = new FiletreeWatcher();
|
||||||
QObject::connect(this->activeRoot, &QtShell::destroyed, this, &RootWrapper::destroy);
|
this->configWatcher->addPath(QFileInfo(this->rootPath).dir().path());
|
||||||
|
|
||||||
|
QObject::connect(this->root, &ShellRoot::configChanged, this, &RootWrapper::onConfigChanged);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->configWatcher,
|
||||||
|
&FiletreeWatcher::fileChanged,
|
||||||
|
this,
|
||||||
|
&RootWrapper::onWatchedFilesChanged
|
||||||
|
);
|
||||||
|
} else if (!config.mWatchFiles && this->configWatcher != nullptr) {
|
||||||
|
this->configWatcher->deleteLater();
|
||||||
|
this->configWatcher = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* RootWrapper::scavengeTargetFor(QObject* /* child */) { return this->activeRoot; }
|
void RootWrapper::onWatchedFilesChanged() { this->reloadGraph(false); }
|
||||||
|
|
||||||
void RootWrapper::destroy() { this->deleteLater(); }
|
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qobjectdefs.h>
|
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qurl.h>
|
#include <qurl.h>
|
||||||
|
|
||||||
#include "scavenge.hpp"
|
#include "scavenge.hpp"
|
||||||
#include "shell.hpp"
|
#include "shell.hpp"
|
||||||
|
#include "watcher.hpp"
|
||||||
|
|
||||||
class RootWrapper: public QObject, virtual public Scavengeable {
|
class RootWrapper: public QObject, virtual public Scavengeable {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RootWrapper(QUrl rootUrl);
|
explicit RootWrapper(QString rootPath);
|
||||||
|
|
||||||
void reloadGraph(bool hard);
|
|
||||||
void changeRoot(QtShell* newRoot);
|
|
||||||
|
|
||||||
QObject* scavengeTargetFor(QObject* child) override;
|
QObject* scavengeTargetFor(QObject* child) override;
|
||||||
|
|
||||||
|
void reloadGraph(bool hard);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void destroy();
|
void onConfigChanged();
|
||||||
|
void onWatchedFilesChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUrl rootUrl;
|
QString rootPath;
|
||||||
QQmlEngine engine;
|
QQmlEngine engine;
|
||||||
QtShell* activeRoot = nullptr;
|
ShellRoot* root = nullptr;
|
||||||
QMetaObject::Connection destroyConnection;
|
FiletreeWatcher* configWatcher = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,16 +3,17 @@
|
||||||
|
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmllist.h>
|
#include <qqmllist.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
void QtShell::earlyInit(QObject* old) {
|
void ShellRoot::earlyInit(QObject* old) {
|
||||||
auto* oldshell = qobject_cast<QtShell*>(old);
|
auto* oldshell = qobject_cast<ShellRoot*>(old);
|
||||||
|
|
||||||
if (oldshell != nullptr) {
|
if (oldshell != nullptr) {
|
||||||
this->scavengeableChildren = std::move(oldshell->children);
|
this->scavengeableChildren = std::move(oldshell->children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* QtShell::scavengeTargetFor(QObject* /* child */) {
|
QObject* ShellRoot::scavengeTargetFor(QObject* /* child */) {
|
||||||
if (this->scavengeableChildren.length() > this->children.length()) {
|
if (this->scavengeableChildren.length() > this->children.length()) {
|
||||||
return this->scavengeableChildren[this->children.length()];
|
return this->scavengeableChildren[this->children.length()];
|
||||||
}
|
}
|
||||||
|
@ -20,11 +21,19 @@ QObject* QtShell::scavengeTargetFor(QObject* /* child */) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<QObject> QtShell::components() {
|
void ShellRoot::setConfig(ShellConfig config) {
|
||||||
|
this->mConfig = config;
|
||||||
|
|
||||||
|
emit this->configChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellConfig ShellRoot::config() const { return this->mConfig; }
|
||||||
|
|
||||||
|
QQmlListProperty<QObject> ShellRoot::components() {
|
||||||
return QQmlListProperty<QObject>(
|
return QQmlListProperty<QObject>(
|
||||||
this,
|
this,
|
||||||
nullptr,
|
nullptr,
|
||||||
&QtShell::appendComponent,
|
&ShellRoot::appendComponent,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
|
@ -33,8 +42,8 @@ QQmlListProperty<QObject> QtShell::components() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtShell::appendComponent(QQmlListProperty<QObject>* list, QObject* component) {
|
void ShellRoot::appendComponent(QQmlListProperty<QObject>* list, QObject* component) {
|
||||||
auto* shell = static_cast<QtShell*>(list->object); // NOLINT
|
auto* shell = static_cast<ShellRoot*>(list->object); // NOLINT
|
||||||
component->setParent(shell);
|
component->setParent(shell);
|
||||||
shell->children.append(component);
|
shell->children.append(component);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,24 +9,40 @@
|
||||||
|
|
||||||
#include "scavenge.hpp"
|
#include "scavenge.hpp"
|
||||||
|
|
||||||
class QtShell: public Scavenger, virtual public Scavengeable {
|
class ShellConfig {
|
||||||
|
Q_GADGET;
|
||||||
|
Q_PROPERTY(bool watchFiles MEMBER mWatchFiles);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool mWatchFiles = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShellRoot: public Scavenger, virtual public Scavengeable {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
Q_PROPERTY(ShellConfig config READ config WRITE setConfig);
|
||||||
Q_PROPERTY(QQmlListProperty<QObject> components READ components FINAL);
|
Q_PROPERTY(QQmlListProperty<QObject> components READ components FINAL);
|
||||||
Q_CLASSINFO("DefaultProperty", "components");
|
Q_CLASSINFO("DefaultProperty", "components");
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit QtShell(QObject* parent = nullptr): Scavenger(parent) {}
|
explicit ShellRoot(QObject* parent = nullptr): Scavenger(parent) {}
|
||||||
|
|
||||||
void earlyInit(QObject* old) override;
|
void earlyInit(QObject* old) override;
|
||||||
QObject* scavengeTargetFor(QObject* child) override;
|
QObject* scavengeTargetFor(QObject* child) override;
|
||||||
|
|
||||||
|
void setConfig(ShellConfig config);
|
||||||
|
[[nodiscard]] ShellConfig config() const;
|
||||||
|
|
||||||
QQmlListProperty<QObject> components();
|
QQmlListProperty<QObject> components();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void configChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void appendComponent(QQmlListProperty<QObject>* list, QObject* component);
|
static void appendComponent(QQmlListProperty<QObject>* list, QObject* component);
|
||||||
|
|
||||||
public:
|
ShellConfig mConfig;
|
||||||
|
|
||||||
// track only the children assigned to `components` in order
|
// track only the children assigned to `components` in order
|
||||||
QList<QObject*> children;
|
QList<QObject*> children;
|
||||||
QList<QObject*> scavengeableChildren;
|
QList<QObject*> scavengeableChildren;
|
||||||
|
|
38
src/cpp/watcher.cpp
Normal file
38
src/cpp/watcher.cpp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#include "watcher.hpp"
|
||||||
|
|
||||||
|
#include <qdir.h>
|
||||||
|
#include <qfileinfo.h>
|
||||||
|
#include <qfilesystemwatcher.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
FiletreeWatcher::FiletreeWatcher(QObject* parent): QObject(parent) {
|
||||||
|
QObject::connect(
|
||||||
|
&this->watcher,
|
||||||
|
&QFileSystemWatcher::fileChanged,
|
||||||
|
this,
|
||||||
|
&FiletreeWatcher::onFileChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
&this->watcher,
|
||||||
|
&QFileSystemWatcher::directoryChanged,
|
||||||
|
this,
|
||||||
|
&FiletreeWatcher::onDirectoryChanged
|
||||||
|
);
|
||||||
|
}
|
||||||
|
void FiletreeWatcher::addPath(const QString& path) {
|
||||||
|
this->watcher.addPath(path);
|
||||||
|
|
||||||
|
if (QFileInfo(path).isDir()) {
|
||||||
|
auto dir = QDir(path);
|
||||||
|
|
||||||
|
for (auto& entry: dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot)) {
|
||||||
|
this->addPath(dir.filePath(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FiletreeWatcher::onDirectoryChanged(const QString& path) { this->addPath(path); }
|
||||||
|
|
||||||
|
void FiletreeWatcher::onFileChanged(const QString& path) { emit this->fileChanged(path); }
|
24
src/cpp/watcher.hpp
Normal file
24
src/cpp/watcher.hpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qdir.h>
|
||||||
|
#include <qfilesystemwatcher.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
|
||||||
|
class FiletreeWatcher: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FiletreeWatcher(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
void addPath(const QString& path);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void fileChanged(const QString& path);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onDirectoryChanged(const QString& path);
|
||||||
|
void onFileChanged(const QString& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFileSystemWatcher watcher;
|
||||||
|
};
|
Loading…
Reference in a new issue