quickshell/src/cpp/rootwrapper.cpp
outfoxxed 1da43be6c0
feat: completely redesign hot reloader
The hot reloader previously attempted to figure out which parent a
component would attach to as it loaded. This was fairly error prone as
it was heuristic based and didn't work as soon as you split
definitions into multiple QML files.

The new hot reloader functions by first completely building the widget
tree, then applying the old tree to the first tree and pulling out
usable values. Proxy windows now wait to appear until being reloaded.

Additionally added support for `reloadableId` to help match a
Reloadable to its value in the previous widget tree.
2024-02-16 17:09:50 -08:00

81 lines
2 KiB
C++

#include "rootwrapper.hpp"
#include <cstdlib>
#include <utility>
#include <qfileinfo.h>
#include <qlogging.h>
#include <qobject.h>
#include <qqmlcomponent.h>
#include <qqmlengine.h>
#include <qurl.h>
#include "shell.hpp"
#include "watcher.hpp"
RootWrapper::RootWrapper(QString rootPath):
QObject(nullptr), rootPath(std::move(rootPath)), engine(this) {
this->reloadGraph(true);
if (this->root == nullptr) {
qCritical() << "could not create scene graph, exiting";
exit(-1); // NOLINT
}
}
void RootWrapper::reloadGraph(bool hard) {
if (this->root != nullptr) {
this->engine.clearComponentCache();
}
auto component = QQmlComponent(&this->engine, QUrl::fromLocalFile(this->rootPath));
auto* obj = component.beginCreate(this->engine.rootContext());
if (obj == nullptr) {
qWarning() << component.errorString().toStdString().c_str();
qWarning() << "failed to create root component";
return;
}
auto* newRoot = qobject_cast<ShellRoot*>(obj);
if (newRoot == nullptr) {
qWarning() << "root component was not a QuickShell.ShellRoot";
delete obj;
return;
}
component.completeCreate();
newRoot->onReload(hard ? nullptr : this->root);
if (this->root != nullptr) {
this->root->deleteLater();
this->root = nullptr;
}
this->root = newRoot;
this->onConfigChanged();
}
void RootWrapper::onConfigChanged() {
auto config = this->root->config();
if (config.mWatchFiles && this->configWatcher == nullptr) {
this->configWatcher = new FiletreeWatcher();
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;
}
}
void RootWrapper::onWatchedFilesChanged() { this->reloadGraph(false); }