forked from quickshell/quickshell
		
	core: synthesized qmldir files and new qml scanning strategy
This commit is contained in:
		
							parent
							
								
									1687ff3614
								
							
						
					
					
						commit
						ffbdac9977
					
				
					 14 changed files with 354 additions and 90 deletions
				
			
		| 
						 | 
					@ -9,7 +9,6 @@ qt_add_library(quickshell-core STATIC
 | 
				
			||||||
	rootwrapper.cpp
 | 
						rootwrapper.cpp
 | 
				
			||||||
	qmlglobal.cpp
 | 
						qmlglobal.cpp
 | 
				
			||||||
	qmlscreen.cpp
 | 
						qmlscreen.cpp
 | 
				
			||||||
	watcher.cpp
 | 
					 | 
				
			||||||
	region.cpp
 | 
						region.cpp
 | 
				
			||||||
	persistentprops.cpp
 | 
						persistentprops.cpp
 | 
				
			||||||
	windowinterface.cpp
 | 
						windowinterface.cpp
 | 
				
			||||||
| 
						 | 
					@ -18,6 +17,8 @@ qt_add_library(quickshell-core STATIC
 | 
				
			||||||
	popupwindow.cpp
 | 
						popupwindow.cpp
 | 
				
			||||||
	singleton.cpp
 | 
						singleton.cpp
 | 
				
			||||||
	generation.cpp
 | 
						generation.cpp
 | 
				
			||||||
 | 
						scan.cpp
 | 
				
			||||||
 | 
						qsintercept.cpp
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}")
 | 
					set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/core/enginecontext.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/core/enginecontext.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qsintercept.hpp"
 | 
				
			||||||
 | 
					#include "scan.hpp"
 | 
				
			||||||
 | 
					#include "singleton.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EngineContext {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit EngineContext(const QmlScanner& scanner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						const QmlScanner& scanner;
 | 
				
			||||||
 | 
						QQmlEngine engine;
 | 
				
			||||||
 | 
						QsInterceptNetworkAccessManagerFactory interceptFactory;
 | 
				
			||||||
 | 
						SingletonRegistry singletonRegistry;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
#include "generation.hpp"
 | 
					#include "generation.hpp"
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <qcontainerfwd.h>
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
#include <qcoreapplication.h>
 | 
					#include <qcoreapplication.h>
 | 
				
			||||||
 | 
					#include <qfilesystemwatcher.h>
 | 
				
			||||||
#include <qhash.h>
 | 
					#include <qhash.h>
 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
#include <qqmlcontext.h>
 | 
					#include <qqmlcontext.h>
 | 
				
			||||||
| 
						 | 
					@ -9,11 +11,19 @@
 | 
				
			||||||
#include <qtimer.h>
 | 
					#include <qtimer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "plugin.hpp"
 | 
					#include "plugin.hpp"
 | 
				
			||||||
 | 
					#include "qsintercept.hpp"
 | 
				
			||||||
#include "reload.hpp"
 | 
					#include "reload.hpp"
 | 
				
			||||||
 | 
					#include "scan.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static QHash<QQmlEngine*, EngineGeneration*> g_generations; // NOLINT
 | 
					static QHash<QQmlEngine*, EngineGeneration*> g_generations; // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EngineGeneration::EngineGeneration() { g_generations.insert(&this->engine, this); }
 | 
					EngineGeneration::EngineGeneration(QmlScanner scanner)
 | 
				
			||||||
 | 
					    : scanner(std::move(scanner))
 | 
				
			||||||
 | 
					    , interceptNetFactory(this->scanner.qmldirIntercepts) {
 | 
				
			||||||
 | 
						g_generations.insert(&this->engine, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->engine.setNetworkAccessManagerFactory(&this->interceptNetFactory);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EngineGeneration::~EngineGeneration() {
 | 
					EngineGeneration::~EngineGeneration() {
 | 
				
			||||||
	g_generations.remove(&this->engine);
 | 
						g_generations.remove(&this->engine);
 | 
				
			||||||
| 
						 | 
					@ -41,6 +51,30 @@ void EngineGeneration::onReload(EngineGeneration* old) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EngineGeneration::setWatchingFiles(bool watching) {
 | 
				
			||||||
 | 
						if (watching) {
 | 
				
			||||||
 | 
							if (this->watcher == nullptr) {
 | 
				
			||||||
 | 
								this->watcher = new QFileSystemWatcher();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (auto& file: this->scanner.scannedFiles) {
 | 
				
			||||||
 | 
									this->watcher->addPath(file);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								QObject::connect(
 | 
				
			||||||
 | 
								    this->watcher,
 | 
				
			||||||
 | 
								    &QFileSystemWatcher::fileChanged,
 | 
				
			||||||
 | 
								    this,
 | 
				
			||||||
 | 
								    &EngineGeneration::filesChanged
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if (this->watcher != nullptr) {
 | 
				
			||||||
 | 
								delete this->watcher;
 | 
				
			||||||
 | 
								this->watcher = nullptr;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EngineGeneration* EngineGeneration::findObjectGeneration(QObject* object) {
 | 
					EngineGeneration* EngineGeneration::findObjectGeneration(QObject* object) {
 | 
				
			||||||
	while (object != nullptr) {
 | 
						while (object != nullptr) {
 | 
				
			||||||
		auto* context = QQmlEngine::contextForObject(object);
 | 
							auto* context = QQmlEngine::contextForObject(object);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,35 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qfilesystemwatcher.h>
 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
#include <qtclasshelpermacros.h>
 | 
					#include <qtclasshelpermacros.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qsintercept.hpp"
 | 
				
			||||||
 | 
					#include "scan.hpp"
 | 
				
			||||||
#include "shell.hpp"
 | 
					#include "shell.hpp"
 | 
				
			||||||
#include "singleton.hpp"
 | 
					#include "singleton.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EngineGeneration {
 | 
					class EngineGeneration: public QObject {
 | 
				
			||||||
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	explicit EngineGeneration();
 | 
						explicit EngineGeneration(QmlScanner scanner);
 | 
				
			||||||
	~EngineGeneration();
 | 
						~EngineGeneration() override;
 | 
				
			||||||
	Q_DISABLE_COPY_MOVE(EngineGeneration);
 | 
						Q_DISABLE_COPY_MOVE(EngineGeneration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// assumes root has been initialized, consumes old generation
 | 
						// assumes root has been initialized, consumes old generation
 | 
				
			||||||
	void onReload(EngineGeneration* old);
 | 
						void onReload(EngineGeneration* old);
 | 
				
			||||||
 | 
						void setWatchingFiles(bool watching);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static EngineGeneration* findObjectGeneration(QObject* object);
 | 
						static EngineGeneration* findObjectGeneration(QObject* object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QmlScanner scanner;
 | 
				
			||||||
 | 
						QsInterceptNetworkAccessManagerFactory interceptNetFactory;
 | 
				
			||||||
	QQmlEngine engine;
 | 
						QQmlEngine engine;
 | 
				
			||||||
	ShellRoot* root = nullptr;
 | 
						ShellRoot* root = nullptr;
 | 
				
			||||||
	SingletonRegistry singletonRegistry;
 | 
						SingletonRegistry singletonRegistry;
 | 
				
			||||||
 | 
						QFileSystemWatcher* watcher = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					signals:
 | 
				
			||||||
 | 
						void filesChanged();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										67
									
								
								src/core/qsintercept.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/core/qsintercept.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					#include "qsintercept.hpp"
 | 
				
			||||||
 | 
					#include <cstring>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qhash.h>
 | 
				
			||||||
 | 
					#include <qiodevice.h>
 | 
				
			||||||
 | 
					#include <qlogging.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <qminmax.h>
 | 
				
			||||||
 | 
					#include <qnetworkaccessmanager.h>
 | 
				
			||||||
 | 
					#include <qnetworkrequest.h>
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qstring.h>
 | 
				
			||||||
 | 
					#include <qtypes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_LOGGING_CATEGORY(logQsIntercept, "quickshell.interceptor", QtWarningMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QsInterceptDataReply::QsInterceptDataReply(const QString& qmldir, QObject* parent)
 | 
				
			||||||
 | 
					    : QNetworkReply(parent)
 | 
				
			||||||
 | 
					    , content(qmldir.toUtf8()) {
 | 
				
			||||||
 | 
						this->setOpenMode(QIODevice::ReadOnly);
 | 
				
			||||||
 | 
						this->setFinished(true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qint64 QsInterceptDataReply::readData(char* data, qint64 maxSize) {
 | 
				
			||||||
 | 
						auto size = qMin(maxSize, this->content.length() - this->offset);
 | 
				
			||||||
 | 
						if (size == 0) return -1;
 | 
				
			||||||
 | 
						memcpy(data, this->content.constData() + this->offset, size); // NOLINT
 | 
				
			||||||
 | 
						this->offset += size;
 | 
				
			||||||
 | 
						return size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QsInterceptNetworkAccessManager::QsInterceptNetworkAccessManager(
 | 
				
			||||||
 | 
					    const QHash<QString, QString>& qmldirIntercepts,
 | 
				
			||||||
 | 
					    QObject* parent
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					    : QNetworkAccessManager(parent)
 | 
				
			||||||
 | 
					    , qmldirIntercepts(qmldirIntercepts) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QNetworkReply* QsInterceptNetworkAccessManager::createRequest(
 | 
				
			||||||
 | 
					    QNetworkAccessManager::Operation op,
 | 
				
			||||||
 | 
					    const QNetworkRequest& req,
 | 
				
			||||||
 | 
					    QIODevice* outgoingData
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						auto url = req.url();
 | 
				
			||||||
 | 
						if (url.scheme() == "qsintercept") {
 | 
				
			||||||
 | 
							auto path = url.path();
 | 
				
			||||||
 | 
							qCDebug(logQsIntercept) << "Got intercept for" << path << "contains"
 | 
				
			||||||
 | 
							                        << this->qmldirIntercepts.value(path);
 | 
				
			||||||
 | 
							auto qmldir = this->qmldirIntercepts.value(path);
 | 
				
			||||||
 | 
							if (qmldir != nullptr) {
 | 
				
			||||||
 | 
								return new QsInterceptDataReply(qmldir, this);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto fileReq = req;
 | 
				
			||||||
 | 
							auto fileUrl = req.url();
 | 
				
			||||||
 | 
							fileUrl.setScheme("file");
 | 
				
			||||||
 | 
							qCDebug(logQsIntercept) << "Passing through intercept" << url << "to" << fileUrl;
 | 
				
			||||||
 | 
							fileReq.setUrl(fileUrl);
 | 
				
			||||||
 | 
							return this->QNetworkAccessManager::createRequest(op, fileReq, outgoingData);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return this->QNetworkAccessManager::createRequest(op, req, outgoingData);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QNetworkAccessManager* QsInterceptNetworkAccessManagerFactory::create(QObject* parent) {
 | 
				
			||||||
 | 
						return new QsInterceptNetworkAccessManager(this->qmldirIntercepts, parent);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										57
									
								
								src/core/qsintercept.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/core/qsintercept.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,57 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qhash.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <qnetworkaccessmanager.h>
 | 
				
			||||||
 | 
					#include <qnetworkreply.h>
 | 
				
			||||||
 | 
					#include <qnetworkrequest.h>
 | 
				
			||||||
 | 
					#include <qqmlnetworkaccessmanagerfactory.h>
 | 
				
			||||||
 | 
					#include <qurl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_DECLARE_LOGGING_CATEGORY(logQsIntercept);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QsInterceptDataReply: public QNetworkReply {
 | 
				
			||||||
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						QsInterceptDataReply(const QString& qmldir, QObject* parent = nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qint64 readData(char* data, qint64 maxSize) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
						void abort() override {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						qint64 offset = 0;
 | 
				
			||||||
 | 
						QByteArray content;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QsInterceptNetworkAccessManager: public QNetworkAccessManager {
 | 
				
			||||||
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						QsInterceptNetworkAccessManager(
 | 
				
			||||||
 | 
						    const QHash<QString, QString>& qmldirIntercepts,
 | 
				
			||||||
 | 
						    QObject* parent = nullptr
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						QNetworkReply* createRequest(
 | 
				
			||||||
 | 
						    QNetworkAccessManager::Operation op,
 | 
				
			||||||
 | 
						    const QNetworkRequest& req,
 | 
				
			||||||
 | 
						    QIODevice* outgoingData = nullptr
 | 
				
			||||||
 | 
						) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						const QHash<QString, QString>& qmldirIntercepts;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QsInterceptNetworkAccessManagerFactory: public QQmlNetworkAccessManagerFactory {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						QsInterceptNetworkAccessManagerFactory(const QHash<QString, QString>& qmldirIntercepts)
 | 
				
			||||||
 | 
						    : qmldirIntercepts(qmldirIntercepts) {}
 | 
				
			||||||
 | 
						QNetworkAccessManager* create(QObject* parent) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						const QHash<QString, QString>& qmldirIntercepts;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -12,8 +12,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "generation.hpp"
 | 
					#include "generation.hpp"
 | 
				
			||||||
#include "qmlglobal.hpp"
 | 
					#include "qmlglobal.hpp"
 | 
				
			||||||
 | 
					#include "scan.hpp"
 | 
				
			||||||
#include "shell.hpp"
 | 
					#include "shell.hpp"
 | 
				
			||||||
#include "watcher.hpp"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
RootWrapper::RootWrapper(QString rootPath)
 | 
					RootWrapper::RootWrapper(QString rootPath)
 | 
				
			||||||
    : QObject(nullptr)
 | 
					    : QObject(nullptr)
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,10 @@ RootWrapper::~RootWrapper() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RootWrapper::reloadGraph(bool hard) {
 | 
					void RootWrapper::reloadGraph(bool hard) {
 | 
				
			||||||
	auto* generation = new EngineGeneration();
 | 
						auto scanner = QmlScanner();
 | 
				
			||||||
 | 
						scanner.scanQmlFile(this->rootPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* generation = new EngineGeneration(std::move(scanner));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// todo: move into EngineGeneration
 | 
						// todo: move into EngineGeneration
 | 
				
			||||||
	if (this->generation != nullptr) {
 | 
						if (this->generation != nullptr) {
 | 
				
			||||||
| 
						 | 
					@ -51,7 +54,10 @@ void RootWrapper::reloadGraph(bool hard) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QDir::setCurrent(this->originalWorkingDirectory);
 | 
						QDir::setCurrent(this->originalWorkingDirectory);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto component = QQmlComponent(&generation->engine, QUrl::fromLocalFile(this->rootPath));
 | 
						auto url = QUrl::fromLocalFile(this->rootPath);
 | 
				
			||||||
 | 
						// unless the original file comes from the qsintercept scheme
 | 
				
			||||||
 | 
						url.setScheme("qsintercept");
 | 
				
			||||||
 | 
						auto component = QQmlComponent(&generation->engine, url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto* obj = component.beginCreate(generation->engine.rootContext());
 | 
						auto* obj = component.beginCreate(generation->engine.rootContext());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,25 +86,20 @@ void RootWrapper::reloadGraph(bool hard) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	qInfo() << "Configuration Loaded";
 | 
						qInfo() << "Configuration Loaded";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QObject::connect(
 | 
				
			||||||
 | 
						    this->generation,
 | 
				
			||||||
 | 
						    &EngineGeneration::filesChanged,
 | 
				
			||||||
 | 
						    this,
 | 
				
			||||||
 | 
						    &RootWrapper::onWatchedFilesChanged
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->onWatchFilesChanged();
 | 
						this->onWatchFilesChanged();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RootWrapper::onWatchFilesChanged() {
 | 
					void RootWrapper::onWatchFilesChanged() {
 | 
				
			||||||
	auto watchFiles = QuickshellSettings::instance()->watchFiles();
 | 
						auto watchFiles = QuickshellSettings::instance()->watchFiles();
 | 
				
			||||||
 | 
						if (this->generation != nullptr) {
 | 
				
			||||||
	if (watchFiles && this->configWatcher == nullptr) {
 | 
							this->generation->setWatchingFiles(watchFiles);
 | 
				
			||||||
		this->configWatcher = new FiletreeWatcher();
 | 
					 | 
				
			||||||
		this->configWatcher->addPath(QFileInfo(this->rootPath).dir().path());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		QObject::connect(
 | 
					 | 
				
			||||||
		    this->configWatcher,
 | 
					 | 
				
			||||||
		    &FiletreeWatcher::fileChanged,
 | 
					 | 
				
			||||||
		    this,
 | 
					 | 
				
			||||||
		    &RootWrapper::onWatchedFilesChanged
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	} else if (!watchFiles && this->configWatcher != nullptr) {
 | 
					 | 
				
			||||||
		this->configWatcher->deleteLater();
 | 
					 | 
				
			||||||
		this->configWatcher = nullptr;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,6 @@
 | 
				
			||||||
#include <qurl.h>
 | 
					#include <qurl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "generation.hpp"
 | 
					#include "generation.hpp"
 | 
				
			||||||
#include "watcher.hpp"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RootWrapper: public QObject {
 | 
					class RootWrapper: public QObject {
 | 
				
			||||||
	Q_OBJECT;
 | 
						Q_OBJECT;
 | 
				
			||||||
| 
						 | 
					@ -26,6 +25,5 @@ private slots:
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	QString rootPath;
 | 
						QString rootPath;
 | 
				
			||||||
	EngineGeneration* generation = nullptr;
 | 
						EngineGeneration* generation = nullptr;
 | 
				
			||||||
	FiletreeWatcher* configWatcher = nullptr;
 | 
					 | 
				
			||||||
	QString originalWorkingDirectory;
 | 
						QString originalWorkingDirectory;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										119
									
								
								src/core/scan.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/core/scan.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,119 @@
 | 
				
			||||||
 | 
					#include "scan.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qdir.h>
 | 
				
			||||||
 | 
					#include <qfileinfo.h>
 | 
				
			||||||
 | 
					#include <qlogging.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <qstring.h>
 | 
				
			||||||
 | 
					#include <qtextstream.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_LOGGING_CATEGORY(logQmlScanner, "quickshell.qmlscanner", QtWarningMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void QmlScanner::scanDir(const QString& path) {
 | 
				
			||||||
 | 
						if (this->scannedDirs.contains(path)) return;
 | 
				
			||||||
 | 
						this->scannedDirs.push_back(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCDebug(logQmlScanner) << "Scanning directory" << path;
 | 
				
			||||||
 | 
						auto dir = QDir(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool seenQmldir = false;
 | 
				
			||||||
 | 
						auto singletons = QVector<QString>();
 | 
				
			||||||
 | 
						auto entries = QVector<QString>();
 | 
				
			||||||
 | 
						for (auto& entry: dir.entryList(QDir::Files | QDir::NoDotAndDotDot)) {
 | 
				
			||||||
 | 
							if (entry == "qmldir") {
 | 
				
			||||||
 | 
								qCDebug(logQmlScanner
 | 
				
			||||||
 | 
								) << "Found qmldir file, qmldir synthesization will be disabled for directory"
 | 
				
			||||||
 | 
								  << path;
 | 
				
			||||||
 | 
								seenQmldir = true;
 | 
				
			||||||
 | 
							} else if (entry.at(0).isUpper() && entry.endsWith(".qml")) {
 | 
				
			||||||
 | 
								if (this->scanQmlFile(dir.filePath(entry))) {
 | 
				
			||||||
 | 
									singletons.push_back(entry);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									entries.push_back(entry);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Due to the qsintercept:// protocol a qmldir is always required, even without singletons.
 | 
				
			||||||
 | 
						if (!seenQmldir) {
 | 
				
			||||||
 | 
							qCDebug(logQmlScanner) << "Synthesizing qmldir for directory" << path << "singletons"
 | 
				
			||||||
 | 
							                       << singletons;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							QString qmldir;
 | 
				
			||||||
 | 
							auto stream = QTextStream(&qmldir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (auto& singleton: singletons) {
 | 
				
			||||||
 | 
								stream << "singleton " << singleton.sliced(0, singleton.length() - 4) << " 1.0 " << singleton
 | 
				
			||||||
 | 
								       << "\n";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (auto& entry: entries) {
 | 
				
			||||||
 | 
								stream << entry.sliced(0, entry.length() - 4) << " 1.0 " << entry << "\n";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							qCDebug(logQmlScanner) << "Synthesized qmldir for" << path << qPrintable("\n" + qmldir);
 | 
				
			||||||
 | 
							this->qmldirIntercepts.insert(QDir(path).filePath("qmldir"), qmldir);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QmlScanner::scanQmlFile(const QString& path) {
 | 
				
			||||||
 | 
						if (this->scannedFiles.contains(path)) return false;
 | 
				
			||||||
 | 
						this->scannedFiles.push_back(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCDebug(logQmlScanner) << "Scanning qml file" << path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto file = QFile(path);
 | 
				
			||||||
 | 
						if (!file.open(QFile::ReadOnly | QFile::Text)) {
 | 
				
			||||||
 | 
							qCWarning(logQmlScanner) << "Failed to open file" << path;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto stream = QTextStream(&file);
 | 
				
			||||||
 | 
						auto imports = QVector<QString>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool singleton = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (!stream.atEnd()) {
 | 
				
			||||||
 | 
							auto line = stream.readLine().trimmed();
 | 
				
			||||||
 | 
							if (!singleton && line == "pragma Singleton") {
 | 
				
			||||||
 | 
								qCDebug(logQmlScanner) << "Discovered singleton" << path;
 | 
				
			||||||
 | 
								singleton = true;
 | 
				
			||||||
 | 
							} else if (line.startsWith("import")) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto startQuot = line.indexOf('"');
 | 
				
			||||||
 | 
								if (startQuot == -1 || line.length() < startQuot + 3) continue;
 | 
				
			||||||
 | 
								auto endQuot = line.indexOf('"', startQuot + 1);
 | 
				
			||||||
 | 
								if (endQuot == -1) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto name = line.sliced(startQuot + 1, endQuot - startQuot - 1);
 | 
				
			||||||
 | 
								imports.push_back(name);
 | 
				
			||||||
 | 
							} else if (line.contains('{')) break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						file.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (logQmlScanner().isDebugEnabled() && !imports.isEmpty()) {
 | 
				
			||||||
 | 
							qCDebug(logQmlScanner) << "Found imports" << imports;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto currentdir = QDir(QFileInfo(path).canonicalPath());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// the root can never be a singleton so it dosent matter if we skip it
 | 
				
			||||||
 | 
						this->scanDir(currentdir.path());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto& import: imports) {
 | 
				
			||||||
 | 
							auto ipath = currentdir.filePath(import);
 | 
				
			||||||
 | 
							auto cpath = QFileInfo(ipath).canonicalFilePath();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (cpath.isEmpty()) {
 | 
				
			||||||
 | 
								qCWarning(logQmlScanner) << "Ignoring unresolvable import" << ipath << "from" << path;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (import.endsWith(".js")) this->scannedFiles.push_back(cpath);
 | 
				
			||||||
 | 
							else this->scanDir(cpath);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return singleton;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/core/scan.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/core/scan.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qhash.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <qvector.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_DECLARE_LOGGING_CATEGORY(logQmlScanner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// expects canonical paths
 | 
				
			||||||
 | 
					class QmlScanner {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						void scanDir(const QString& path);
 | 
				
			||||||
 | 
						// returns if the file has a singleton
 | 
				
			||||||
 | 
						bool scanQmlFile(const QString& path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QVector<QString> scannedDirs;
 | 
				
			||||||
 | 
						QVector<QString> scannedFiles;
 | 
				
			||||||
 | 
						QHash<QString, QString> qmldirIntercepts;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,10 @@
 | 
				
			||||||
#include "singleton.hpp"
 | 
					#include "singleton.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qhash.h>
 | 
				
			||||||
#include <qlogging.h>
 | 
					#include <qlogging.h>
 | 
				
			||||||
#include <qmap.h>
 | 
					 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
#include <qqmlcontext.h>
 | 
					#include <qqmlcontext.h>
 | 
				
			||||||
#include <qqmlengine.h>
 | 
					#include <qqmlengine.h>
 | 
				
			||||||
#include <qurl.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "generation.hpp"
 | 
					#include "generation.hpp"
 | 
				
			||||||
#include "reload.hpp"
 | 
					#include "reload.hpp"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,11 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qhash.h>
 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qqmlengine.h>
 | 
				
			||||||
#include <qqmlintegration.h>
 | 
					#include <qqmlintegration.h>
 | 
				
			||||||
#include <qtclasshelpermacros.h>
 | 
					 | 
				
			||||||
#include <qtmetamacros.h>
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
 | 
					#include <qtypes.h>
 | 
				
			||||||
#include <qurl.h>
 | 
					#include <qurl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "reload.hpp"
 | 
					#include "reload.hpp"
 | 
				
			||||||
| 
						 | 
					@ -26,5 +28,5 @@ public:
 | 
				
			||||||
	void onReload(SingletonRegistry* old);
 | 
						void onReload(SingletonRegistry* old);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	QMap<QUrl, Singleton*> registry;
 | 
						QHash<QUrl, Singleton*> registry;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,38 +0,0 @@
 | 
				
			||||||
#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); }
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,24 +0,0 @@
 | 
				
			||||||
#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…
	
	Add table
		Add a link
		
	
		Reference in a new issue