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));
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(); }

View file

@ -13,6 +13,7 @@
#include <qqmlengine.h>
#include <qqmlincubator.h>
#include <qtimer.h>
#include <qtmetamacros.h>
#include "iconimageprovider.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::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->reloadComplete = true;
emit this->reloadFinished();
if (old != nullptr) {
QTimer::singleShot(0, [this, old]() {

View file

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

View file

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

View file

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

View file

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

View file

@ -4,6 +4,43 @@
#include <qobject.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) {
auto* old = qobject_cast<ReloadPropagator*>(oldInstance);
@ -13,7 +50,7 @@ void ReloadPropagator::onReload(QObject* oldInstance) {
auto* oldChild = old == nullptr || old->mChildren.length() <= i
? nullptr
: qobject_cast<Reloadable*>(old->mChildren.at(i));
newChild->onReload(oldChild);
newChild->reload(oldChild);
} else {
Reloadable::reloadRecursive(newChild, oldInstance);
}

View file

@ -7,6 +7,8 @@
#include <qqmlparserstatus.h>
#include <qtmetamacros.h>
class EngineGeneration;
///! The base class of all types that can be reloaded.
/// Reloadables will attempt to take specific state from previous config revisions if possible.
/// Some examples are [ProxyWindowBase] and [PersistentProperties]
@ -56,14 +58,10 @@ class Reloadable
public:
explicit Reloadable(QObject* parent = nullptr): QObject(parent) {}
// 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;
void reload(QObject* oldInstance = nullptr);
// TODO: onReload runs after initialization for reloadable objects created late
void classBegin() override {}
void componentComplete() override {}
void classBegin() override {};
void componentComplete() override;
// Reload objects in the parent->child graph recursively.
static void reloadRecursive(QObject* newObj, QObject* oldRoot);
@ -71,6 +69,17 @@ public:
static void reloadChildrenRecursive(QObject* newRoot, QObject* oldRoot);
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:
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) {
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.setVisible(true);
parent.onReload(nullptr);
popup.onReload(nullptr);
parent.reload();
popup.reload();
QVERIFY(popup.isVisible());
QVERIFY(popup.backingWindow()->isVisible());
@ -36,8 +36,8 @@ void TestPopupWindow::reloadReparent() { // NOLINT
popup.setParentWindow(&parent);
popup.setVisible(true);
parent.onReload(nullptr);
popup.onReload(nullptr);
parent.reload();
popup.reload();
// second generation
auto newParent = ProxyWindowBase();
@ -51,8 +51,8 @@ void TestPopupWindow::reloadReparent() { // NOLINT
auto spy = QSignalSpy(oldWindow, &QWindow::visibleChanged);
newParent.onReload(&parent);
newPopup.onReload(&popup);
newParent.reload(&parent);
newPopup.reload(&popup);
QVERIFY(newPopup.isVisible());
QVERIFY(newPopup.backingWindow()->isVisible());
@ -69,15 +69,15 @@ void TestPopupWindow::reloadUnparent() { // NOLINT
popup.setParentWindow(&parent);
popup.setVisible(true);
parent.onReload(nullptr);
popup.onReload(nullptr);
parent.reload();
popup.reload();
// second generation
auto newPopup = ProxyPopupWindow();
// parent not set
newPopup.setVisible(true);
newPopup.onReload(&popup);
newPopup.reload(&popup);
QVERIFY(!newPopup.isVisible());
QVERIFY(!newPopup.backingWindow()->isVisible());
@ -88,7 +88,7 @@ void TestPopupWindow::invisibleWithoutParent() { // NOLINT
auto popup = ProxyPopupWindow();
popup.setVisible(true);
popup.onReload(nullptr);
popup.reload();
QVERIFY(!popup.isVisible());
}
@ -102,8 +102,8 @@ void TestPopupWindow::moveWithParent() { // NOLINT
popup.setRelativeY(10);
popup.setVisible(true);
parent.onReload(nullptr);
popup.onReload(nullptr);
parent.reload();
popup.reload();
QCOMPARE(popup.x(), parent.x() + 10);
QCOMPARE(popup.y(), parent.y() + 10);
@ -121,8 +121,8 @@ void TestPopupWindow::attachParentLate() { // NOLINT
popup.setVisible(true);
parent.onReload(nullptr);
popup.onReload(nullptr);
parent.reload();
popup.reload();
QVERIFY(!popup.isVisible());
@ -139,14 +139,14 @@ void TestPopupWindow::reparentLate() { // NOLINT
popup.setParentWindow(&parent);
popup.setVisible(true);
parent.onReload(nullptr);
popup.onReload(nullptr);
parent.reload();
popup.reload();
QCOMPARE(popup.x(), parent.x());
QCOMPARE(popup.y(), parent.y());
auto parent2 = ProxyWindowBase();
parent2.onReload(nullptr);
parent2.reload();
parent2.backingWindow()->setX(10);
parent2.backingWindow()->setY(10);
@ -166,8 +166,8 @@ void TestPopupWindow::xMigrationFix() { // NOLINT
popup.setParentWindow(&parent);
popup.setVisible(true);
parent.onReload(nullptr);
popup.onReload(nullptr);
parent.reload();
popup.reload();
QCOMPARE(popup.x(), parent.x());

View file

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

View file

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

View file

@ -197,7 +197,7 @@ void WaylandPanelInterface::onReload(QObject* oldInstance) {
QQmlEngine::setContextForObject(this->layer, QQmlEngine::contextForObject(this));
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(); }