mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-26 03:15:21 +02:00
Build a better event bridge
This commit is contained in:
parent
8bbabf186f
commit
a43fde0803
10 changed files with 108 additions and 192 deletions
|
@ -10,50 +10,11 @@
|
||||||
|
|
||||||
var EventBridge;
|
var EventBridge;
|
||||||
|
|
||||||
EventBridgeConnectionProxy = function(parent) {
|
openEventBridge = function(callback) {
|
||||||
this.parent = parent;
|
new QWebChannel(qt.webChannelTransport, function(channel) {
|
||||||
this.realSignal = this.parent.realBridge.scriptEventReceived
|
console.log("uid " + EventBridgeUid);
|
||||||
this.webWindowId = this.parent.webWindow.windowId;
|
EventBridge = channel.objects[EventBridgeUid];
|
||||||
}
|
callback(EventBridge);
|
||||||
|
|
||||||
EventBridgeConnectionProxy.prototype.connect = function(callback) {
|
|
||||||
var that = this;
|
|
||||||
this.realSignal.connect(function(id, message) {
|
|
||||||
if (id === that.webWindowId) { callback(message); }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
EventBridgeProxy = function(webWindow) {
|
|
||||||
this.webWindow = webWindow;
|
|
||||||
this.realBridge = this.webWindow.eventBridge;
|
|
||||||
this.scriptEventReceived = new EventBridgeConnectionProxy(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
EventBridgeProxy.prototype.emitWebEvent = function(data) {
|
|
||||||
this.realBridge.emitWebEvent(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
openEventBridge = function(callback) {
|
|
||||||
EVENT_BRIDGE_URI = "ws://localhost:51016";
|
|
||||||
socket = new WebSocket(this.EVENT_BRIDGE_URI);
|
|
||||||
|
|
||||||
socket.onclose = function() {
|
|
||||||
console.error("web channel closed");
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.onerror = function(error) {
|
|
||||||
console.error("web channel error: " + error);
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.onopen = function() {
|
|
||||||
channel = new QWebChannel(socket, function(channel) {
|
|
||||||
console.log("Document url is " + document.URL);
|
|
||||||
var webWindow = channel.objects[document.URL.toLowerCase()];
|
|
||||||
console.log("WebWindow is " + webWindow)
|
|
||||||
eventBridgeProxy = new EventBridgeProxy(webWindow);
|
|
||||||
EventBridge = eventBridgeProxy;
|
|
||||||
if (callback) { callback(eventBridgeProxy); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,17 @@
|
||||||
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
|
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
|
||||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var myBridge;
|
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
openEventBridge(function(eventBridge) {
|
openEventBridge(function() {
|
||||||
myBridge = eventBridge;
|
EventBridge.scriptEventReceived.connect(function(message) {
|
||||||
myBridge.scriptEventReceived.connect(function(message) {
|
|
||||||
console.log("HTML side received message: " + message);
|
console.log("HTML side received message: " + message);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
testClick = function() {
|
testClick = function() {
|
||||||
myBridge.emitWebEvent("HTML side sending message - button click");
|
EventBridge.emitWebEvent(["Foo", "Bar", { "baz": 1} ]);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -8,26 +8,14 @@ webWindow.eventBridge.webEventReceived.connect(function(data) {
|
||||||
print("JS Side event received: " + data);
|
print("JS Side event received: " + data);
|
||||||
});
|
});
|
||||||
|
|
||||||
var titles = ["A", "B", "C"];
|
|
||||||
var titleIndex = 0;
|
|
||||||
|
|
||||||
Script.setInterval(function() {
|
Script.setInterval(function() {
|
||||||
webWindow.eventBridge.emitScriptEvent("JS Event sent");
|
var message = [ Math.random(), Math.random() ];
|
||||||
var size = webWindow.size;
|
print("JS Side sending: " + message);
|
||||||
var position = webWindow.position;
|
webWindow.emitScriptEvent(message);
|
||||||
print("Window url: " + webWindow.url)
|
}, 5 * 1000);
|
||||||
print("Window visible: " + webWindow.visible)
|
|
||||||
print("Window size: " + size.x + "x" + size.y)
|
|
||||||
print("Window pos: " + position.x + "x" + position.y)
|
|
||||||
webWindow.setVisible(!webWindow.visible);
|
|
||||||
webWindow.setTitle(titles[titleIndex]);
|
|
||||||
webWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100);
|
|
||||||
titleIndex += 1;
|
|
||||||
titleIndex %= titles.length;
|
|
||||||
}, 2 * 1000);
|
|
||||||
|
|
||||||
Script.setTimeout(function() {
|
Script.scriptEnding.connect(function(){
|
||||||
print("Closing script");
|
|
||||||
webWindow.close();
|
webWindow.close();
|
||||||
Script.stop();
|
webWindow.deleteLater();
|
||||||
}, 15 * 1000)
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtWebEngine 1.1
|
import QtWebEngine 1.1
|
||||||
|
import QtWebChannel 1.0
|
||||||
|
|
||||||
import "windows" as Windows
|
import "windows" as Windows
|
||||||
import "controls" as Controls
|
import "controls" as Controls
|
||||||
|
@ -15,11 +16,22 @@ Windows.Window {
|
||||||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||||
destroyOnCloseButton: false
|
destroyOnCloseButton: false
|
||||||
property alias source: webview.url
|
property alias source: webview.url
|
||||||
|
property alias webChannel: webview.webChannel
|
||||||
|
// A unique identifier to let the HTML JS find the event bridge
|
||||||
|
// object (our C++ wrapper)
|
||||||
|
property string uid;
|
||||||
|
|
||||||
|
// This is for JS/QML communication, which is unused in a WebWindow,
|
||||||
|
// but not having this here results in spurious warnings about a
|
||||||
|
// missing signal
|
||||||
|
signal sendToScript(var message);
|
||||||
|
|
||||||
Controls.WebView {
|
Controls.WebView {
|
||||||
id: webview
|
id: webview
|
||||||
url: "about:blank"
|
url: "about:blank"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
|
onUrlChanged: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||||
|
Component.onCompleted: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||||
}
|
}
|
||||||
} // dialog
|
} // dialog
|
||||||
|
|
|
@ -37,14 +37,33 @@ Windows.Window {
|
||||||
Repeater {
|
Repeater {
|
||||||
model: 4
|
model: 4
|
||||||
Tab {
|
Tab {
|
||||||
|
// Force loading of the content even if the tab is not visible
|
||||||
|
// (required for letting the C++ code access the webview)
|
||||||
active: true
|
active: true
|
||||||
enabled: false;
|
enabled: false
|
||||||
// we need to store the original url here for future identification
|
|
||||||
property string originalUrl: "";
|
property string originalUrl: "";
|
||||||
onEnabledChanged: toolWindow.updateVisiblity();
|
|
||||||
Controls.WebView {
|
Controls.WebView {
|
||||||
id: webView;
|
id: webView;
|
||||||
|
// we need to store the original url here for future identification
|
||||||
|
// A unique identifier to let the HTML JS find the event bridge
|
||||||
|
// object (our C++ wrapper)
|
||||||
|
property string uid;
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
// This is for JS/QML communication, which is unused in a WebWindow,
|
||||||
|
// but not having this here results in spurious warnings about a
|
||||||
|
// missing signal
|
||||||
|
signal sendToScript(var message);
|
||||||
|
|
||||||
|
onUrlChanged: webView.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||||
|
onEnabledChanged: toolWindow.updateVisiblity();
|
||||||
|
onLoadingChanged: {
|
||||||
|
if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
|
||||||
|
webView.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,20 +132,23 @@ Windows.Window {
|
||||||
|
|
||||||
var tab = tabView.getTab(index);
|
var tab = tabView.getTab(index);
|
||||||
tab.title = "";
|
tab.title = "";
|
||||||
tab.originalUrl = "";
|
|
||||||
tab.enabled = false;
|
tab.enabled = false;
|
||||||
|
tab.originalUrl = "";
|
||||||
|
tab.item.url = "about:blank";
|
||||||
|
tab.item.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addWebTab(properties) {
|
function addWebTab(properties) {
|
||||||
if (!properties.source) {
|
if (!properties.source) {
|
||||||
console.warn("Attempted to open Web Tool Pane without URL")
|
console.warn("Attempted to open Web Tool Pane without URL");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingTabIndex = findIndexForUrl(properties.source);
|
var existingTabIndex = findIndexForUrl(properties.source);
|
||||||
if (existingTabIndex >= 0) {
|
if (existingTabIndex >= 0) {
|
||||||
console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source)
|
console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source);
|
||||||
return tabView.getTab(existingTabIndex);
|
var tab = tabView.getTab(existingTabIndex);
|
||||||
|
return tab.item;
|
||||||
}
|
}
|
||||||
|
|
||||||
var freeTabIndex = findFreeTab();
|
var freeTabIndex = findFreeTab();
|
||||||
|
@ -135,25 +157,22 @@ Windows.Window {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTab = tabView.getTab(freeTabIndex);
|
|
||||||
newTab.title = properties.title || "Unknown";
|
|
||||||
newTab.originalUrl = properties.source;
|
|
||||||
newTab.item.url = properties.source;
|
|
||||||
newTab.active = true;
|
|
||||||
|
|
||||||
if (properties.width) {
|
if (properties.width) {
|
||||||
tabView.width = Math.min(Math.max(tabView.width, properties.width),
|
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
|
||||||
toolWindow.maxSize.x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.height) {
|
if (properties.height) {
|
||||||
tabView.height = Math.min(Math.max(tabView.height, properties.height),
|
tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y);
|
||||||
toolWindow.maxSize.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Updating visibility based on child tab added");
|
var tab = tabView.getTab(freeTabIndex);
|
||||||
newTab.enabledChanged.connect(updateVisiblity)
|
tab.title = properties.title || "Unknown";
|
||||||
updateVisiblity();
|
tab.enabled = true;
|
||||||
return newTab
|
tab.originalUrl = properties.source;
|
||||||
|
|
||||||
|
var result = tab.item;
|
||||||
|
result.enabled = true;
|
||||||
|
result.url = properties.source;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ WebEngineView {
|
||||||
request.openIn(newWindow.webView)
|
request.openIn(newWindow.webView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This breaks the webchannel used for passing messages. Fixed in Qt 5.6
|
||||||
profile: desktop.browserProfile
|
// See https://bugreports.qt.io/browse/QTBUG-49521
|
||||||
|
//profile: desktop.browserProfile
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include <QtQml/QQmlContext>
|
#include <QtQml/QQmlContext>
|
||||||
|
|
||||||
|
#include <QtWebChannel/QWebChannel>
|
||||||
|
|
||||||
#include <QtScript/QScriptContext>
|
#include <QtScript/QScriptContext>
|
||||||
#include <QtScript/QScriptEngine>
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
|
@ -35,8 +37,29 @@ QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngi
|
||||||
}
|
}
|
||||||
|
|
||||||
QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) {
|
QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) {
|
||||||
|
_uid = QUuid::createUuid().toString();
|
||||||
|
asQuickItem()->setProperty("uid", _uid);
|
||||||
|
auto webchannelVar = qmlWindow->property("webChannel");
|
||||||
|
_webchannel = qvariant_cast<QWebChannel*>(webchannelVar);
|
||||||
|
Q_ASSERT(_webchannel);
|
||||||
|
_webchannel->registerObject(_uid, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::emitScriptEvent(const QVariant& scriptMessage) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, scriptMessage));
|
||||||
|
} else {
|
||||||
|
emit scriptEventReceived(scriptMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::emitWebEvent(const QVariant& webMessage) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage));
|
||||||
|
} else {
|
||||||
|
emit webEventReceived(webMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString QmlWebWindowClass::getURL() const {
|
QString QmlWebWindowClass::getURL() const {
|
||||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||||
|
|
|
@ -11,10 +11,13 @@
|
||||||
|
|
||||||
#include "QmlWindowClass.h"
|
#include "QmlWindowClass.h"
|
||||||
|
|
||||||
|
class QWebChannel;
|
||||||
|
|
||||||
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
||||||
class QmlWebWindowClass : public QmlWindowClass {
|
class QmlWebWindowClass : public QmlWindowClass {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString url READ getURL CONSTANT)
|
Q_PROPERTY(QString url READ getURL CONSTANT)
|
||||||
|
Q_PROPERTY(QString uid READ getUid CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||||
|
@ -23,9 +26,18 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
QString getURL() const;
|
QString getURL() const;
|
||||||
void setURL(const QString& url);
|
void setURL(const QString& url);
|
||||||
|
const QString& getUid() const { return _uid; }
|
||||||
|
void emitScriptEvent(const QVariant& scriptMessage);
|
||||||
|
void emitWebEvent(const QVariant& webMessage);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void urlChanged();
|
void urlChanged();
|
||||||
|
void scriptEventReceived(const QVariant& message);
|
||||||
|
void webEventReceived(const QVariant& message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _uid;
|
||||||
|
QWebChannel* _webchannel { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,10 +26,6 @@
|
||||||
|
|
||||||
#include "OffscreenUi.h"
|
#include "OffscreenUi.h"
|
||||||
|
|
||||||
QWebSocketServer* QmlWindowClass::_webChannelServer { nullptr };
|
|
||||||
static QWebChannel webChannel;
|
|
||||||
static const uint16_t WEB_CHANNEL_PORT = 51016;
|
|
||||||
static std::atomic<int> nextWindowId;
|
|
||||||
static const char* const SOURCE_PROPERTY = "source";
|
static const char* const SOURCE_PROPERTY = "source";
|
||||||
static const char* const TITLE_PROPERTY = "title";
|
static const char* const TITLE_PROPERTY = "title";
|
||||||
static const char* const WIDTH_PROPERTY = "width";
|
static const char* const WIDTH_PROPERTY = "width";
|
||||||
|
@ -37,54 +33,6 @@ static const char* const HEIGHT_PROPERTY = "height";
|
||||||
static const char* const VISIBILE_PROPERTY = "visible";
|
static const char* const VISIBILE_PROPERTY = "visible";
|
||||||
static const char* const TOOLWINDOW_PROPERTY = "toolWindow";
|
static const char* const TOOLWINDOW_PROPERTY = "toolWindow";
|
||||||
|
|
||||||
void QmlScriptEventBridge::emitWebEvent(const QString& data) {
|
|
||||||
QMetaObject::invokeMethod(this, "webEventReceived", Qt::QueuedConnection, Q_ARG(QString, data));
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlScriptEventBridge::emitScriptEvent(const QString& data) {
|
|
||||||
QMetaObject::invokeMethod(this, "scriptEventReceived", Qt::QueuedConnection,
|
|
||||||
Q_ARG(int, _webWindow->getWindowId()), Q_ARG(QString, data));
|
|
||||||
}
|
|
||||||
|
|
||||||
class QmlWebTransport : public QWebChannelAbstractTransport {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
QmlWebTransport(QWebSocket* webSocket) : _webSocket(webSocket) {
|
|
||||||
// Translate from the websocket layer to the webchannel layer
|
|
||||||
connect(webSocket, &QWebSocket::textMessageReceived, [this](const QString& message) {
|
|
||||||
QJsonParseError error;
|
|
||||||
QJsonDocument document = QJsonDocument::fromJson(message.toUtf8(), &error);
|
|
||||||
if (error.error || !document.isObject()) {
|
|
||||||
qWarning() << "Unable to parse incoming JSON message" << message;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
emit messageReceived(document.object(), this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void sendMessage(const QJsonObject &message) override {
|
|
||||||
// Translate from the webchannel layer to the websocket layer
|
|
||||||
_webSocket->sendTextMessage(QJsonDocument(message).toJson(QJsonDocument::Compact));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QWebSocket* const _webSocket;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void QmlWindowClass::setupServer() {
|
|
||||||
if (!_webChannelServer) {
|
|
||||||
_webChannelServer = new QWebSocketServer("EventBridge Server", QWebSocketServer::NonSecureMode);
|
|
||||||
if (!_webChannelServer->listen(QHostAddress::LocalHost, WEB_CHANNEL_PORT)) {
|
|
||||||
qFatal("Failed to open web socket server.");
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject::connect(_webChannelServer, &QWebSocketServer::newConnection, [] {
|
|
||||||
webChannel.connectTo(new QmlWebTransport(_webChannelServer->nextPendingConnection()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
||||||
QScriptContext* context, QScriptEngine* engine,
|
QScriptContext* context, QScriptEngine* engine,
|
||||||
std::function<QmlWindowClass*(QObject*)> builder)
|
std::function<QmlWindowClass*(QObject*)> builder)
|
||||||
|
@ -168,10 +116,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
offscreenUi->returnFromUiThread([&] {
|
offscreenUi->returnFromUiThread([&] {
|
||||||
setupServer();
|
|
||||||
retVal = builder(newTab);
|
retVal = builder(newTab);
|
||||||
retVal->_toolWindow = true;
|
retVal->_toolWindow = true;
|
||||||
registerObject(url.toLower(), retVal);
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -179,10 +125,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
||||||
QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection,
|
||||||
Q_ARG(const QString&, qmlSource),
|
Q_ARG(const QString&, qmlSource),
|
||||||
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
|
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
|
||||||
setupServer();
|
|
||||||
retVal = builder(object);
|
retVal = builder(object);
|
||||||
context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
|
context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
|
||||||
registerObject(url.toLower(), retVal);
|
|
||||||
if (!title.isEmpty()) {
|
if (!title.isEmpty()) {
|
||||||
retVal->setTitle(title);
|
retVal->setTitle(title);
|
||||||
}
|
}
|
||||||
|
@ -209,10 +153,7 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine*
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QmlWindowClass::QmlWindowClass(QObject* qmlWindow)
|
QmlWindowClass::QmlWindowClass(QObject* qmlWindow) : _qmlWindow(qmlWindow) {
|
||||||
: _windowId(++nextWindowId), _qmlWindow(qmlWindow)
|
|
||||||
{
|
|
||||||
qDebug() << "Created window with ID " << _windowId;
|
|
||||||
Q_ASSERT(_qmlWindow);
|
Q_ASSERT(_qmlWindow);
|
||||||
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
|
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
|
||||||
// Forward messages received from QML on to the script
|
// Forward messages received from QML on to the script
|
||||||
|
@ -228,14 +169,6 @@ QmlWindowClass::~QmlWindowClass() {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlWindowClass::registerObject(const QString& name, QObject* object) {
|
|
||||||
webChannel.registerObject(name, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlWindowClass::deregisterObject(QObject* object) {
|
|
||||||
webChannel.deregisterObject(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
QQuickItem* QmlWindowClass::asQuickItem() const {
|
QQuickItem* QmlWindowClass::asQuickItem() const {
|
||||||
if (_toolWindow) {
|
if (_toolWindow) {
|
||||||
return DependencyManager::get<OffscreenUi>()->getToolWindow();
|
return DependencyManager::get<OffscreenUi>()->getToolWindow();
|
||||||
|
|
|
@ -13,38 +13,16 @@
|
||||||
#include <QtCore/QPointer>
|
#include <QtCore/QPointer>
|
||||||
#include <QtScript/QScriptValue>
|
#include <QtScript/QScriptValue>
|
||||||
#include <QtQuick/QQuickItem>
|
#include <QtQuick/QQuickItem>
|
||||||
#include <QtWebChannel/QWebChannelAbstractTransport>
|
|
||||||
|
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
|
|
||||||
class QScriptEngine;
|
class QScriptEngine;
|
||||||
class QScriptContext;
|
class QScriptContext;
|
||||||
class QmlWindowClass;
|
|
||||||
class QWebSocketServer;
|
|
||||||
class QWebSocket;
|
|
||||||
|
|
||||||
class QmlScriptEventBridge : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
QmlScriptEventBridge(const QmlWindowClass* webWindow) : _webWindow(webWindow) {}
|
|
||||||
|
|
||||||
public slots :
|
|
||||||
void emitWebEvent(const QString& data);
|
|
||||||
void emitScriptEvent(const QString& data);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void webEventReceived(const QString& data);
|
|
||||||
void scriptEventReceived(int windowId, const QString& data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const QmlWindowClass* _webWindow { nullptr };
|
|
||||||
QWebSocket *_socket { nullptr };
|
|
||||||
};
|
|
||||||
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
||||||
class QmlWindowClass : public QObject {
|
class QmlWindowClass : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT)
|
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT)
|
||||||
Q_PROPERTY(int windowId READ getWindowId CONSTANT)
|
|
||||||
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged)
|
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged)
|
||||||
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
|
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
|
||||||
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
|
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
|
||||||
|
@ -69,8 +47,7 @@ public slots:
|
||||||
|
|
||||||
Q_INVOKABLE void raise();
|
Q_INVOKABLE void raise();
|
||||||
Q_INVOKABLE void close();
|
Q_INVOKABLE void close();
|
||||||
Q_INVOKABLE int getWindowId() const { return _windowId; };
|
Q_INVOKABLE QObject* getEventBridge() { return this; };
|
||||||
Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; };
|
|
||||||
|
|
||||||
// Scripts can use this to send a message to the QML object
|
// Scripts can use this to send a message to the QML object
|
||||||
void sendToQml(const QVariant& message);
|
void sendToQml(const QVariant& message);
|
||||||
|
@ -92,18 +69,12 @@ protected:
|
||||||
static QScriptValue internalConstructor(const QString& qmlSource,
|
static QScriptValue internalConstructor(const QString& qmlSource,
|
||||||
QScriptContext* context, QScriptEngine* engine,
|
QScriptContext* context, QScriptEngine* engine,
|
||||||
std::function<QmlWindowClass*(QObject*)> function);
|
std::function<QmlWindowClass*(QObject*)> function);
|
||||||
static void setupServer();
|
|
||||||
static void registerObject(const QString& name, QObject* object);
|
|
||||||
static void deregisterObject(QObject* object);
|
|
||||||
static QWebSocketServer* _webChannelServer;
|
|
||||||
|
|
||||||
QQuickItem* asQuickItem() const;
|
QQuickItem* asQuickItem() const;
|
||||||
QmlScriptEventBridge* const _eventBridge { new QmlScriptEventBridge(this) };
|
|
||||||
|
|
||||||
// FIXME needs to be initialized in the ctor once we have support
|
// FIXME needs to be initialized in the ctor once we have support
|
||||||
// for tool window panes in QML
|
// for tool window panes in QML
|
||||||
bool _toolWindow { false };
|
bool _toolWindow { false };
|
||||||
const int _windowId;
|
|
||||||
QPointer<QObject> _qmlWindow;
|
QPointer<QObject> _qmlWindow;
|
||||||
QString _source;
|
QString _source;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue