mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 11:17:32 +02:00
Merge pull request #11613 from jherico/feature/qml_whitelist
Support for custom functionality for specific QML URLs
This commit is contained in:
commit
25818fc5e9
2 changed files with 85 additions and 17 deletions
|
@ -41,6 +41,7 @@
|
||||||
#include <gl/OffscreenGLCanvas.h>
|
#include <gl/OffscreenGLCanvas.h>
|
||||||
#include <gl/GLHelpers.h>
|
#include <gl/GLHelpers.h>
|
||||||
#include <gl/Context.h>
|
#include <gl/Context.h>
|
||||||
|
#include <shared/ReadWriteLockable.h>
|
||||||
|
|
||||||
#include "types/FileTypeProfile.h"
|
#include "types/FileTypeProfile.h"
|
||||||
#include "types/HFWebEngineProfile.h"
|
#include "types/HFWebEngineProfile.h"
|
||||||
|
@ -52,6 +53,53 @@ Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml")
|
||||||
Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl")
|
Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl")
|
||||||
Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus")
|
Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus")
|
||||||
|
|
||||||
|
|
||||||
|
class OffscreenQmlWhitelist : public Dependency, private ReadWriteLockable {
|
||||||
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void addWhitelistContextHandler(const std::initializer_list<QUrl>& urls, const QmlContextCallback& callback) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
for (const auto& url : urls) {
|
||||||
|
_callbacks[url].push_back(callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QmlContextCallback> getCallbacksForUrl(const QUrl& url) const {
|
||||||
|
return resultWithReadLock<QList<QmlContextCallback>>([&] {
|
||||||
|
QList<QmlContextCallback> result;
|
||||||
|
auto itr = _callbacks.find(url);
|
||||||
|
if (_callbacks.end() != itr) {
|
||||||
|
result = *itr;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QHash<QUrl, QList<QmlContextCallback>> _callbacks;
|
||||||
|
};
|
||||||
|
|
||||||
|
QSharedPointer<OffscreenQmlWhitelist> getQmlWhitelist() {
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, [&] {
|
||||||
|
DependencyManager::set<OffscreenQmlWhitelist>();
|
||||||
|
});
|
||||||
|
|
||||||
|
return DependencyManager::get<OffscreenQmlWhitelist>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OffscreenQmlSurface::addWhitelistContextHandler(const std::initializer_list<QUrl>& urls, const QmlContextCallback& callback) {
|
||||||
|
getQmlWhitelist()->addWhitelistContextHandler(urls, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QmlContextCallback OffscreenQmlSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QObject*) {};
|
||||||
|
|
||||||
struct TextureSet {
|
struct TextureSet {
|
||||||
// The number of surfaces with this size
|
// The number of surfaces with this size
|
||||||
size_t count { 0 };
|
size_t count { 0 };
|
||||||
|
@ -640,18 +688,26 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
|
||||||
_qmlContext->setBaseUrl(baseUrl);
|
_qmlContext->setBaseUrl(baseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> onQmlLoadedCallback) {
|
void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, const QmlContextCallback& onQmlLoadedCallback) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
qCWarning(uiLogging) << "Called load on a non-surface thread";
|
qCWarning(uiLogging) << "Called load on a non-surface thread";
|
||||||
}
|
}
|
||||||
// Synchronous loading may take a while; restart the deadlock timer
|
// Synchronous loading may take a while; restart the deadlock timer
|
||||||
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
|
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
|
||||||
|
|
||||||
|
// Get any whitelist functionality
|
||||||
|
QList<QmlContextCallback> callbacks = getQmlWhitelist()->getCallbacksForUrl(qmlSource);
|
||||||
|
// If we have whitelisted content, we must load a new context
|
||||||
|
createNewContext |= !callbacks.empty();
|
||||||
|
callbacks.push_back(onQmlLoadedCallback);
|
||||||
|
|
||||||
QQmlContext* targetContext = _qmlContext;
|
QQmlContext* targetContext = _qmlContext;
|
||||||
if (_rootItem && createNewContext) {
|
if (_rootItem && createNewContext) {
|
||||||
targetContext = new QQmlContext(targetContext);
|
targetContext = new QQmlContext(targetContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME eliminate loading of relative file paths for QML
|
||||||
QUrl finalQmlSource = qmlSource;
|
QUrl finalQmlSource = qmlSource;
|
||||||
if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) {
|
if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) {
|
||||||
finalQmlSource = _qmlContext->resolvedUrl(qmlSource);
|
finalQmlSource = _qmlContext->resolvedUrl(qmlSource);
|
||||||
|
@ -659,29 +715,32 @@ void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std
|
||||||
|
|
||||||
auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous);
|
auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous);
|
||||||
if (qmlComponent->isLoading()) {
|
if (qmlComponent->isLoading()) {
|
||||||
connect(qmlComponent, &QQmlComponent::statusChanged, this,
|
connect(qmlComponent, &QQmlComponent::statusChanged, this, [=](QQmlComponent::Status) {
|
||||||
[this, qmlComponent, targetContext, onQmlLoadedCallback](QQmlComponent::Status) {
|
finishQmlLoad(qmlComponent, targetContext, callbacks);
|
||||||
finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback);
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback);
|
finishQmlLoad(qmlComponent, targetContext, callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> onQmlLoadedCallback) {
|
void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextCallback& onQmlLoadedCallback) {
|
||||||
load(qmlSource, true, onQmlLoadedCallback);
|
load(qmlSource, true, onQmlLoadedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> onQmlLoadedCallback) {
|
void OffscreenQmlSurface::load(const QUrl& qmlSource, const QmlContextCallback& onQmlLoadedCallback) {
|
||||||
load(qmlSource, false, onQmlLoadedCallback);
|
load(qmlSource, false, onQmlLoadedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OffscreenQmlSurface::load(const QString& qmlSourceFile, const QmlContextCallback& onQmlLoadedCallback) {
|
||||||
|
return load(QUrl(qmlSourceFile), onQmlLoadedCallback);
|
||||||
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::clearCache() {
|
void OffscreenQmlSurface::clearCache() {
|
||||||
_qmlContext->engine()->clearComponentCache();
|
_qmlContext->engine()->clearComponentCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> onQmlLoadedCallback) {
|
void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, const QList<QmlContextCallback>& callbacks) {
|
||||||
disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
|
disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
|
||||||
if (qmlComponent->isError()) {
|
if (qmlComponent->isError()) {
|
||||||
for (const auto& error : qmlComponent->errors()) {
|
for (const auto& error : qmlComponent->errors()) {
|
||||||
|
@ -716,7 +775,9 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
|
||||||
// Make sure we will call callback for this codepath
|
// Make sure we will call callback for this codepath
|
||||||
// Call this before qmlComponent->completeCreate() otherwise ghost window appears
|
// Call this before qmlComponent->completeCreate() otherwise ghost window appears
|
||||||
if (newItem && _rootItem) {
|
if (newItem && _rootItem) {
|
||||||
onQmlLoadedCallback(qmlContext, newObject);
|
for (const auto& callback : callbacks) {
|
||||||
|
callback(qmlContext, newObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* eventBridge = qmlContext->contextProperty("eventBridge").value<QObject*>();
|
QObject* eventBridge = qmlContext->contextProperty("eventBridge").value<QObject*>();
|
||||||
|
@ -751,8 +812,11 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
|
||||||
_rootItem = newItem;
|
_rootItem = newItem;
|
||||||
_rootItem->setParentItem(_quickWindow->contentItem());
|
_rootItem->setParentItem(_quickWindow->contentItem());
|
||||||
_rootItem->setSize(_quickWindow->renderTargetSize());
|
_rootItem->setSize(_quickWindow->renderTargetSize());
|
||||||
|
|
||||||
// Call this callback after rootitem is set, otherwise VrMenu wont work
|
// Call this callback after rootitem is set, otherwise VrMenu wont work
|
||||||
onQmlLoadedCallback(qmlContext, newObject);
|
for (const auto& callback : callbacks) {
|
||||||
|
callback(qmlContext, newObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::updateQuick() {
|
void OffscreenQmlSurface::updateQuick() {
|
||||||
|
|
|
@ -35,12 +35,18 @@ class QQuickItem;
|
||||||
// one copy in flight, and one copy being used by the receiver
|
// one copy in flight, and one copy being used by the receiver
|
||||||
#define GPU_RESOURCE_BUFFER_SIZE 3
|
#define GPU_RESOURCE_BUFFER_SIZE 3
|
||||||
|
|
||||||
|
using QmlContextCallback = std::function<void(QQmlContext*, QObject*)>;
|
||||||
|
|
||||||
class OffscreenQmlSurface : public QObject {
|
class OffscreenQmlSurface : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
|
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
|
||||||
public:
|
public:
|
||||||
static void setSharedContext(QOpenGLContext* context);
|
static void setSharedContext(QOpenGLContext* context);
|
||||||
|
|
||||||
|
static QmlContextCallback DEFAULT_CONTEXT_CALLBACK;
|
||||||
|
static void addWhitelistContextHandler(const std::initializer_list<QUrl>& urls, const QmlContextCallback& callback);
|
||||||
|
static void addWhitelistContextHandler(const QUrl& url, const QmlContextCallback& callback) { addWhitelistContextHandler({ { url } }, callback); };
|
||||||
|
|
||||||
OffscreenQmlSurface();
|
OffscreenQmlSurface();
|
||||||
virtual ~OffscreenQmlSurface();
|
virtual ~OffscreenQmlSurface();
|
||||||
|
|
||||||
|
@ -50,12 +56,10 @@ public:
|
||||||
void resize(const QSize& size, bool forceResize = false);
|
void resize(const QSize& size, bool forceResize = false);
|
||||||
QSize size() const;
|
QSize size() const;
|
||||||
|
|
||||||
Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> onQmlLoadedCallback = [](QQmlContext*, QObject*) {});
|
Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK);
|
||||||
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> onQmlLoadedCallback = [](QQmlContext*, QObject*) {});
|
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK);
|
||||||
Q_INVOKABLE void load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> onQmlLoadedCallback = [](QQmlContext*, QObject*) {});
|
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK);
|
||||||
Q_INVOKABLE void load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> onQmlLoadedCallback = [](QQmlContext*, QObject*) {}) {
|
Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK);
|
||||||
return load(QUrl(qmlSourceFile), onQmlLoadedCallback);
|
|
||||||
}
|
|
||||||
void clearCache();
|
void clearCache();
|
||||||
void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; }
|
void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; }
|
||||||
// Optional values for event handling
|
// Optional values for event handling
|
||||||
|
@ -120,7 +124,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
static QOpenGLContext* getSharedContext();
|
static QOpenGLContext* getSharedContext();
|
||||||
|
|
||||||
void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> onQmlLoadedCallback);
|
void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, const QList<QmlContextCallback>& callbacks);
|
||||||
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
|
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
|
||||||
void setupFbo();
|
void setupFbo();
|
||||||
bool allowNewFrame(uint8_t fps);
|
bool allowNewFrame(uint8_t fps);
|
||||||
|
|
Loading…
Reference in a new issue