core/reloader: fix late creation of Reloadable types

This commit is contained in:
outfoxxed 2024-04-19 15:43:26 -07:00
parent 61812343f5
commit 6eb68d2cd7
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
13 changed files with 87 additions and 48 deletions

View file

@ -43,7 +43,7 @@ void FloatingWindowInterface::onReload(QObject* oldInstance) {
QQmlEngine::setContextForObject(this->window, QQmlEngine::contextForObject(this)); QQmlEngine::setContextForObject(this->window, QQmlEngine::contextForObject(this));
auto* old = qobject_cast<FloatingWindowInterface*>(oldInstance); auto* old = qobject_cast<FloatingWindowInterface*>(oldInstance);
this->window->onReload(old != nullptr ? old->window : nullptr); this->window->reload(old != nullptr ? old->window : nullptr);
} }
QQmlListProperty<QObject> FloatingWindowInterface::data() { return this->window->data(); } QQmlListProperty<QObject> FloatingWindowInterface::data() { return this->window->data(); }

View file

@ -13,6 +13,7 @@
#include <qqmlengine.h> #include <qqmlengine.h>
#include <qqmlincubator.h> #include <qqmlincubator.h>
#include <qtimer.h> #include <qtimer.h>
#include <qtmetamacros.h>
#include "iconimageprovider.hpp" #include "iconimageprovider.hpp"
#include "incubator.hpp" #include "incubator.hpp"
@ -54,8 +55,10 @@ void EngineGeneration::onReload(EngineGeneration* old) {
QObject::connect(&this->engine, &QQmlEngine::quit, app, &QCoreApplication::quit); QObject::connect(&this->engine, &QQmlEngine::quit, app, &QCoreApplication::quit);
QObject::connect(&this->engine, &QQmlEngine::exit, app, &QCoreApplication::exit); QObject::connect(&this->engine, &QQmlEngine::exit, app, &QCoreApplication::exit);
this->root->onReload(old == nullptr ? nullptr : old->root); this->root->reload(old == nullptr ? nullptr : old->root);
this->singletonRegistry.onReload(old == nullptr ? nullptr : &old->singletonRegistry); this->singletonRegistry.onReload(old == nullptr ? nullptr : &old->singletonRegistry);
this->reloadComplete = true;
emit this->reloadFinished();
if (old != nullptr) { if (old != nullptr) {
QTimer::singleShot(0, [this, old]() { QTimer::singleShot(0, [this, old]() {

View file

@ -41,9 +41,11 @@ public:
SingletonRegistry singletonRegistry; SingletonRegistry singletonRegistry;
QFileSystemWatcher* watcher = nullptr; QFileSystemWatcher* watcher = nullptr;
DelayedQmlIncubationController delayedIncubationController; DelayedQmlIncubationController delayedIncubationController;
bool reloadComplete = false;
signals: signals:
void filesChanged(); void filesChanged();
void reloadFinished();
private slots: private slots:
void incubationControllerDestroyed(); void incubationControllerDestroyed();

View file

@ -23,13 +23,11 @@ void LazyLoader::onReload(QObject* oldInstance) {
if (this->mItem != nullptr) { if (this->mItem != nullptr) {
if (auto* reloadable = qobject_cast<Reloadable*>(this->mItem)) { if (auto* reloadable = qobject_cast<Reloadable*>(this->mItem)) {
reloadable->onReload(old == nullptr ? nullptr : old->mItem); reloadable->reload(old == nullptr ? nullptr : old->mItem);
} else { } else {
Reloadable::reloadRecursive(this->mItem, old); Reloadable::reloadRecursive(this->mItem, old);
} }
} }
this->postReload = true;
} }
QObject* LazyLoader::item() { QObject* LazyLoader::item() {
@ -48,14 +46,6 @@ void LazyLoader::setItem(QObject* item) {
if (item != nullptr) { if (item != nullptr) {
item->setParent(this); item->setParent(this);
if (this->postReload) {
if (auto* reloadable = qobject_cast<Reloadable*>(this->mItem)) {
reloadable->onReload(nullptr);
} else {
Reloadable::reloadRecursive(this->mItem, nullptr);
}
}
} }
this->targetActive = this->isActive(); this->targetActive = this->isActive();
@ -160,7 +150,7 @@ void LazyLoader::setSource(QString source) {
} }
void LazyLoader::incubateIfReady(bool overrideReloadCheck) { void LazyLoader::incubateIfReady(bool overrideReloadCheck) {
if (!(this->postReload || overrideReloadCheck) || !(this->targetLoading || this->targetActive) if (!(this->reloadComplete || overrideReloadCheck) || !(this->targetLoading || this->targetActive)
|| this->mComponent == nullptr || this->incubator != nullptr) || this->mComponent == nullptr || this->incubator != nullptr)
{ {
return; return;

View file

@ -152,7 +152,6 @@ private:
void incubateIfReady(bool overrideReloadCheck = false); void incubateIfReady(bool overrideReloadCheck = false);
void waitForObjectCreation(); void waitForObjectCreation();
bool postReload = false;
bool targetLoading = false; bool targetLoading = false;
bool targetActive = false; bool targetActive = false;
QObject* mItem = nullptr; QObject* mItem = nullptr;

View file

@ -10,7 +10,6 @@
#include <qguiapplication.h> #include <qguiapplication.h>
#include <qhash.h> #include <qhash.h>
#include <qlogging.h> #include <qlogging.h>
#include <qobject.h>
#include <qquickwindow.h> #include <qquickwindow.h>
#include <qstandardpaths.h> #include <qstandardpaths.h>
#include <qstring.h> #include <qstring.h>

View file

@ -4,6 +4,43 @@
#include <qobject.h> #include <qobject.h>
#include <qqmllist.h> #include <qqmllist.h>
#include "generation.hpp"
void Reloadable::componentComplete() {
this->engineGeneration = EngineGeneration::findObjectGeneration(this);
if (this->engineGeneration != nullptr) {
// When called this way there is no chance a reload will have old data,
// but this will at least help prevent weird behaviors due to never getting a reload.
if (this->engineGeneration->reloadComplete) this->reload();
else {
QObject::connect(
this->engineGeneration,
&EngineGeneration::reloadFinished,
this,
&Reloadable::onReloadFinished
);
}
}
}
void Reloadable::reload(QObject* oldInstance) {
if (this->reloadComplete) return;
this->onReload(oldInstance);
this->reloadComplete = true;
if (this->engineGeneration != nullptr) {
QObject::disconnect(
this->engineGeneration,
&EngineGeneration::reloadFinished,
this,
&Reloadable::onReloadFinished
);
}
}
void Reloadable::onReloadFinished() { this->reload(nullptr); }
void ReloadPropagator::onReload(QObject* oldInstance) { void ReloadPropagator::onReload(QObject* oldInstance) {
auto* old = qobject_cast<ReloadPropagator*>(oldInstance); auto* old = qobject_cast<ReloadPropagator*>(oldInstance);
@ -13,7 +50,7 @@ void ReloadPropagator::onReload(QObject* oldInstance) {
auto* oldChild = old == nullptr || old->mChildren.length() <= i auto* oldChild = old == nullptr || old->mChildren.length() <= i
? nullptr ? nullptr
: qobject_cast<Reloadable*>(old->mChildren.at(i)); : qobject_cast<Reloadable*>(old->mChildren.at(i));
newChild->onReload(oldChild); newChild->reload(oldChild);
} else { } else {
Reloadable::reloadRecursive(newChild, oldInstance); Reloadable::reloadRecursive(newChild, oldInstance);
} }

View file

@ -7,6 +7,8 @@
#include <qqmlparserstatus.h> #include <qqmlparserstatus.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class EngineGeneration;
///! The base class of all types that can be reloaded. ///! The base class of all types that can be reloaded.
/// Reloadables will attempt to take specific state from previous config revisions if possible. /// Reloadables will attempt to take specific state from previous config revisions if possible.
/// Some examples are [ProxyWindowBase] and [PersistentProperties] /// Some examples are [ProxyWindowBase] and [PersistentProperties]
@ -56,14 +58,10 @@ class Reloadable
public: public:
explicit Reloadable(QObject* parent = nullptr): QObject(parent) {} explicit Reloadable(QObject* parent = nullptr): QObject(parent) {}
// Called unconditionally in the reload phase, with nullptr if no source could be determined. void reload(QObject* oldInstance = nullptr);
// If non null the old instance may or may not be of the same type, and should be checked
// by `onReload`.
virtual void onReload(QObject* oldInstance) = 0;
// TODO: onReload runs after initialization for reloadable objects created late void classBegin() override {};
void classBegin() override {} void componentComplete() override;
void componentComplete() override {}
// Reload objects in the parent->child graph recursively. // Reload objects in the parent->child graph recursively.
static void reloadRecursive(QObject* newObj, QObject* oldRoot); static void reloadRecursive(QObject* newObj, QObject* oldRoot);
@ -71,6 +69,17 @@ public:
static void reloadChildrenRecursive(QObject* newRoot, QObject* oldRoot); static void reloadChildrenRecursive(QObject* newRoot, QObject* oldRoot);
QString mReloadableId; QString mReloadableId;
bool reloadComplete = false;
EngineGeneration* engineGeneration = nullptr;
private slots:
void onReloadFinished();
protected:
// Called unconditionally in the reload phase, with nullptr if no source could be determined.
// If non null the old instance may or may not be of the same type, and should be checked
// by `onReload`.
virtual void onReload(QObject* oldInstance) = 0;
private: private:
static QObject* getChildByReloadId(QObject* parent, const QString& reloadId); static QObject* getChildByReloadId(QObject* parent, const QString& reloadId);

View file

@ -48,7 +48,7 @@ void SingletonRegistry::registerSingleton(const QUrl& url, Singleton* singleton)
void SingletonRegistry::onReload(SingletonRegistry* old) { void SingletonRegistry::onReload(SingletonRegistry* old) {
for (auto [url, singleton]: this->registry.asKeyValueRange()) { for (auto [url, singleton]: this->registry.asKeyValueRange()) {
singleton->onReload(old == nullptr ? nullptr : old->registry.value(url)); singleton->reload(old == nullptr ? nullptr : old->registry.value(url));
} }
} }

View file

@ -16,8 +16,8 @@ void TestPopupWindow::initiallyVisible() { // NOLINT
popup.setParentWindow(&parent); popup.setParentWindow(&parent);
popup.setVisible(true); popup.setVisible(true);
parent.onReload(nullptr); parent.reload();
popup.onReload(nullptr); popup.reload();
QVERIFY(popup.isVisible()); QVERIFY(popup.isVisible());
QVERIFY(popup.backingWindow()->isVisible()); QVERIFY(popup.backingWindow()->isVisible());
@ -36,8 +36,8 @@ void TestPopupWindow::reloadReparent() { // NOLINT
popup.setParentWindow(&parent); popup.setParentWindow(&parent);
popup.setVisible(true); popup.setVisible(true);
parent.onReload(nullptr); parent.reload();
popup.onReload(nullptr); popup.reload();
// second generation // second generation
auto newParent = ProxyWindowBase(); auto newParent = ProxyWindowBase();
@ -51,8 +51,8 @@ void TestPopupWindow::reloadReparent() { // NOLINT
auto spy = QSignalSpy(oldWindow, &QWindow::visibleChanged); auto spy = QSignalSpy(oldWindow, &QWindow::visibleChanged);
newParent.onReload(&parent); newParent.reload(&parent);
newPopup.onReload(&popup); newPopup.reload(&popup);
QVERIFY(newPopup.isVisible()); QVERIFY(newPopup.isVisible());
QVERIFY(newPopup.backingWindow()->isVisible()); QVERIFY(newPopup.backingWindow()->isVisible());
@ -69,15 +69,15 @@ void TestPopupWindow::reloadUnparent() { // NOLINT
popup.setParentWindow(&parent); popup.setParentWindow(&parent);
popup.setVisible(true); popup.setVisible(true);
parent.onReload(nullptr); parent.reload();
popup.onReload(nullptr); popup.reload();
// second generation // second generation
auto newPopup = ProxyPopupWindow(); auto newPopup = ProxyPopupWindow();
// parent not set // parent not set
newPopup.setVisible(true); newPopup.setVisible(true);
newPopup.onReload(&popup); newPopup.reload(&popup);
QVERIFY(!newPopup.isVisible()); QVERIFY(!newPopup.isVisible());
QVERIFY(!newPopup.backingWindow()->isVisible()); QVERIFY(!newPopup.backingWindow()->isVisible());
@ -88,7 +88,7 @@ void TestPopupWindow::invisibleWithoutParent() { // NOLINT
auto popup = ProxyPopupWindow(); auto popup = ProxyPopupWindow();
popup.setVisible(true); popup.setVisible(true);
popup.onReload(nullptr); popup.reload();
QVERIFY(!popup.isVisible()); QVERIFY(!popup.isVisible());
} }
@ -102,8 +102,8 @@ void TestPopupWindow::moveWithParent() { // NOLINT
popup.setRelativeY(10); popup.setRelativeY(10);
popup.setVisible(true); popup.setVisible(true);
parent.onReload(nullptr); parent.reload();
popup.onReload(nullptr); popup.reload();
QCOMPARE(popup.x(), parent.x() + 10); QCOMPARE(popup.x(), parent.x() + 10);
QCOMPARE(popup.y(), parent.y() + 10); QCOMPARE(popup.y(), parent.y() + 10);
@ -121,8 +121,8 @@ void TestPopupWindow::attachParentLate() { // NOLINT
popup.setVisible(true); popup.setVisible(true);
parent.onReload(nullptr); parent.reload();
popup.onReload(nullptr); popup.reload();
QVERIFY(!popup.isVisible()); QVERIFY(!popup.isVisible());
@ -139,14 +139,14 @@ void TestPopupWindow::reparentLate() { // NOLINT
popup.setParentWindow(&parent); popup.setParentWindow(&parent);
popup.setVisible(true); popup.setVisible(true);
parent.onReload(nullptr); parent.reload();
popup.onReload(nullptr); popup.reload();
QCOMPARE(popup.x(), parent.x()); QCOMPARE(popup.x(), parent.x());
QCOMPARE(popup.y(), parent.y()); QCOMPARE(popup.y(), parent.y());
auto parent2 = ProxyWindowBase(); auto parent2 = ProxyWindowBase();
parent2.onReload(nullptr); parent2.reload();
parent2.backingWindow()->setX(10); parent2.backingWindow()->setX(10);
parent2.backingWindow()->setY(10); parent2.backingWindow()->setY(10);
@ -166,8 +166,8 @@ void TestPopupWindow::xMigrationFix() { // NOLINT
popup.setParentWindow(&parent); popup.setParentWindow(&parent);
popup.setVisible(true); popup.setVisible(true);
parent.onReload(nullptr); parent.reload();
popup.onReload(nullptr); popup.reload();
QCOMPARE(popup.x(), parent.x()); QCOMPARE(popup.x(), parent.x());

View file

@ -64,7 +64,7 @@ void Variants::onReload(QObject* oldInstance) {
auto* instance = qobject_cast<Reloadable*>(instanceObj); auto* instance = qobject_cast<Reloadable*>(instanceObj);
if (instance != nullptr) instance->onReload(oldInstance); if (instance != nullptr) instance->reload(oldInstance);
else Reloadable::reloadChildrenRecursive(instanceObj, oldInstance); else Reloadable::reloadChildrenRecursive(instanceObj, oldInstance);
} }
@ -168,7 +168,7 @@ void Variants::updateVariants() {
this->mInstances.insert(variant, instance); this->mInstances.insert(variant, instance);
if (this->loaded) { if (this->loaded) {
if (auto* reloadable = qobject_cast<Reloadable*>(instance)) reloadable->onReload(nullptr); if (auto* reloadable = qobject_cast<Reloadable*>(instance)) reloadable->reload(nullptr);
else Reloadable::reloadChildrenRecursive(instance, nullptr); else Reloadable::reloadChildrenRecursive(instance, nullptr);
} }
} }

View file

@ -90,7 +90,7 @@ void WlSessionLock::updateSurfaces(WlSessionLock* old) {
instance->setScreen(screen); instance->setScreen(screen);
auto* oldInstance = old == nullptr ? nullptr : old->surfaces.value(screen, nullptr); auto* oldInstance = old == nullptr ? nullptr : old->surfaces.value(screen, nullptr);
instance->onReload(oldInstance); instance->reload(oldInstance);
this->surfaces[screen] = instance; this->surfaces[screen] = instance;
} }

View file

@ -197,7 +197,7 @@ void WaylandPanelInterface::onReload(QObject* oldInstance) {
QQmlEngine::setContextForObject(this->layer, QQmlEngine::contextForObject(this)); QQmlEngine::setContextForObject(this->layer, QQmlEngine::contextForObject(this));
auto* old = qobject_cast<WaylandPanelInterface*>(oldInstance); auto* old = qobject_cast<WaylandPanelInterface*>(oldInstance);
this->layer->onReload(old != nullptr ? old->layer : nullptr); this->layer->reload(old != nullptr ? old->layer : nullptr);
} }
QQmlListProperty<QObject> WaylandPanelInterface::data() { return this->layer->data(); } QQmlListProperty<QObject> WaylandPanelInterface::data() { return this->layer->data(); }