forked from quickshell/quickshell
		
	core/reloader: fix late creation of Reloadable types
This commit is contained in:
		
							parent
							
								
									61812343f5
								
							
						
					
					
						commit
						6eb68d2cd7
					
				
					 13 changed files with 87 additions and 48 deletions
				
			
		| 
						 | 
				
			
			@ -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(); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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]() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,9 +41,11 @@ public:
 | 
			
		|||
	SingletonRegistry singletonRegistry;
 | 
			
		||||
	QFileSystemWatcher* watcher = nullptr;
 | 
			
		||||
	DelayedQmlIncubationController delayedIncubationController;
 | 
			
		||||
	bool reloadComplete = false;
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void filesChanged();
 | 
			
		||||
	void reloadFinished();
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	void incubationControllerDestroyed();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -152,7 +152,6 @@ private:
 | 
			
		|||
	void incubateIfReady(bool overrideReloadCheck = false);
 | 
			
		||||
	void waitForObjectCreation();
 | 
			
		||||
 | 
			
		||||
	bool postReload = false;
 | 
			
		||||
	bool targetLoading = false;
 | 
			
		||||
	bool targetActive = false;
 | 
			
		||||
	QObject* mItem = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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());
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue