From 1f3adeb66622a0d527e4f4b2307330e60593728d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 18 Dec 2015 17:09:38 -0800 Subject: [PATCH] Revert "Migrate QML based web views to Qt WebEngine" --- .../PackageLibrariesForDeployment.cmake | 2 +- examples/directory.js | 2 +- examples/html/eventBridgeLoader.js | 59 -- examples/html/qmlWebTest.html | 31 - examples/tests/qmlWebTest.js | 32 - interface/CMakeLists.txt | 18 +- interface/resources/qml/Browser.qml | 28 +- interface/resources/qml/InfoView.qml | 16 +- interface/resources/qml/MarketplaceDialog.qml | 37 +- interface/resources/qml/QmlWebWindow.qml | 63 -- interface/resources/qml/TestMenu.qml | 109 +++ interface/resources/qml/WebEntity.qml | 6 +- interface/src/Application.cpp | 17 - libraries/gl/src/gl/OffscreenQmlSurface.cpp | 13 +- libraries/gl/src/gl/OffscreenQmlSurface.h | 4 +- libraries/gl/src/gl/QOpenGLContextWrapper.cpp | 7 +- libraries/gl/src/gl/QOpenGLContextWrapper.h | 6 - libraries/script-engine/CMakeLists.txt | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 3 - libraries/ui/CMakeLists.txt | 2 +- libraries/ui/src/QmlWebWindowClass.cpp | 240 ------- libraries/ui/src/QmlWebWindowClass.h | 104 --- tests/ui/CMakeLists.txt | 19 +- tests/ui/src/main.cpp | 636 +++++------------- 24 files changed, 344 insertions(+), 1112 deletions(-) delete mode 100644 examples/html/eventBridgeLoader.js delete mode 100644 examples/html/qmlWebTest.html delete mode 100644 examples/tests/qmlWebTest.js delete mode 100644 interface/resources/qml/QmlWebWindow.qml delete mode 100644 libraries/ui/src/QmlWebWindowClass.cpp delete mode 100644 libraries/ui/src/QmlWebWindowClass.h diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index 17b5d5f49d..bb0b268dd4 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -40,7 +40,7 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> $" + COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} $<$,$,$>:--release> $" ) elseif (DEFINED BUILD_BUNDLE AND BUILD_BUNDLE AND APPLE) find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH) diff --git a/examples/directory.js b/examples/directory.js index d2a3768051..b1fac19e8b 100644 --- a/examples/directory.js +++ b/examples/directory.js @@ -62,7 +62,7 @@ var directory = (function () { function setUp() { viewport = Controller.getViewportDimensions(); - directoryWindow = new OverlayWebWindow('Directory', DIRECTORY_URL, 900, 700, false); + directoryWindow = new WebWindow('Directory', DIRECTORY_URL, 900, 700, false); directoryWindow.setVisible(false); directoryButton = Overlays.addOverlay("image", { diff --git a/examples/html/eventBridgeLoader.js b/examples/html/eventBridgeLoader.js deleted file mode 100644 index b62e7d9384..0000000000 --- a/examples/html/eventBridgeLoader.js +++ /dev/null @@ -1,59 +0,0 @@ - -//public slots: -// void emitWebEvent(const QString& data); -// void emitScriptEvent(const QString& data); -// -//signals: -// void webEventReceived(const QString& data); -// void scriptEventReceived(const QString& data); -// - -EventBridgeConnectionProxy = function(parent) { - this.parent = parent; - this.realSignal = this.parent.realBridge.scriptEventReceived - this.webWindowId = this.parent.webWindow.windowId; -} - -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); - for(var key in channel.objects){ - console.log("registered object: " + key); - } - var webWindow = channel.objects[document.URL.toLowerCase()]; - console.log("WebWindow is " + webWindow) - eventBridgeProxy = new EventBridgeProxy(webWindow); - if (callback) { callback(eventBridgeProxy); } - }); - } -} - diff --git a/examples/html/qmlWebTest.html b/examples/html/qmlWebTest.html deleted file mode 100644 index e59535701d..0000000000 --- a/examples/html/qmlWebTest.html +++ /dev/null @@ -1,31 +0,0 @@ - - -Properties - - - - - - - - - - - - - diff --git a/examples/tests/qmlWebTest.js b/examples/tests/qmlWebTest.js deleted file mode 100644 index f905e494dc..0000000000 --- a/examples/tests/qmlWebTest.js +++ /dev/null @@ -1,32 +0,0 @@ -print("Launching web window"); - -webWindow = new OverlayWebWindow('Test Event Bridge', "file:///C:/Users/bdavis/Git/hifi/examples/html/qmlWebTest.html", 320, 240, false); -print("JS Side window: " + webWindow); -print("JS Side bridge: " + webWindow.eventBridge); -webWindow.eventBridge.webEventReceived.connect(function(data) { - print("JS Side event received: " + data); -}); - -var titles = ["A", "B", "C"]; -var titleIndex = 0; - -Script.setInterval(function() { - webWindow.eventBridge.emitScriptEvent("JS Event sent"); - var size = webWindow.size; - var position = webWindow.position; - print("Window url: " + webWindow.url) - 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() { - print("Closing script"); - webWindow.close(); - Script.stop(); -}, 15 * 1000) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 1d9557a835..87cf1a384c 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -45,9 +45,7 @@ else () list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP}) endif () -find_package(Qt5 COMPONENTS - Gui Multimedia Network OpenGL Qml Quick Script Svg - WebChannel WebEngine WebEngineWidgets WebKitWidgets WebSockets) +find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets WebSockets) # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) @@ -177,17 +175,9 @@ include_directories("${PROJECT_SOURCE_DIR}/src") target_link_libraries( ${TARGET_NAME} - Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL - Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg - Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets + Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets ) -# Issue causes build failure unless we add this directory. -# See https://bugreports.qt.io/browse/QTBUG-43351 -if (WIN32) - add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine) -endif() - # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) @@ -219,9 +209,5 @@ else (APPLE) endif() endif (APPLE) -if (WIN32) - set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/resources/qml") -endif() - package_libraries_for_deployment() consolidate_stack_components() diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 8f3cd0e1e7..947bf739fc 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -1,6 +1,6 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtWebEngine 1.1 +import QtWebKit 3.0 import "controls" import "styles" @@ -39,10 +39,9 @@ VrDialog { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - anchors.bottom: webview.top + anchors.bottom: scrollView.top color: "white" } - Row { id: buttons spacing: 4 @@ -113,21 +112,25 @@ VrDialog { } } - WebEngineView { - id: webview - url: "http://highfidelity.com" + ScrollView { + id: scrollView anchors.top: buttons.bottom anchors.topMargin: 8 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right - onLoadingChanged: { - if (loadRequest.status == WebEngineView.LoadSucceededStatus) { - addressBar.text = loadRequest.url + WebView { + id: webview + url: "http://highfidelity.com" + anchors.fill: parent + onLoadingChanged: { + if (loadRequest.status == WebView.LoadSucceededStarted) { + addressBar.text = loadRequest.url + } + } + onIconChanged: { + barIcon.source = icon } - } - onIconChanged: { - console.log("New icon: " + icon) } } } // item @@ -143,4 +146,5 @@ VrDialog { break; } } + } // dialog diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml index 75b82342ca..012f04f1fd 100644 --- a/interface/resources/qml/InfoView.qml +++ b/interface/resources/qml/InfoView.qml @@ -2,7 +2,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.3 -import QtWebEngine 1.1 +import QtWebKit 3.0 import "controls" VrDialog { @@ -18,11 +18,15 @@ VrDialog { anchors.margins: parent.margins anchors.topMargin: parent.topMargin - WebEngineView { - id: webview - objectName: "WebView" + ScrollView { anchors.fill: parent - url: infoView.url - } + WebView { + objectName: "WebView" + id: webview + url: infoView.url + anchors.fill: parent + } + } + } } diff --git a/interface/resources/qml/MarketplaceDialog.qml b/interface/resources/qml/MarketplaceDialog.qml index 3a66c5340f..946f32e84a 100644 --- a/interface/resources/qml/MarketplaceDialog.qml +++ b/interface/resources/qml/MarketplaceDialog.qml @@ -2,7 +2,7 @@ import Hifi 1.0 import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.3 -import QtWebEngine 1.1 +import QtWebKit 3.0 import "controls" VrDialog { @@ -24,22 +24,27 @@ VrDialog { anchors.margins: parent.margins anchors.topMargin: parent.topMargin - WebEngineView { - objectName: "WebView" - id: webview - url: "https://metaverse.highfidelity.com/marketplace" + + ScrollView { anchors.fill: parent - onNavigationRequested: { - console.log(request.url) - if (!marketplaceDialog.navigationRequested(request.url)) { - console.log("Application absorbed the request") - request.action = WebView.IgnoreRequest; + WebView { + objectName: "WebView" + id: webview + url: "https://metaverse.highfidelity.com/marketplace" + anchors.fill: parent + onNavigationRequested: { + console.log(request.url) + if (!marketplaceDialog.navigationRequested(request.url)) { + console.log("Application absorbed the request") + request.action = WebView.IgnoreRequest; + return; + } + console.log("Application passed on the request") + request.action = WebView.AcceptRequest; return; - } - console.log("Application passed on the request") - request.action = WebView.AcceptRequest; - return; + } } - } - } + } + + } } diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml deleted file mode 100644 index 9664c0edb8..0000000000 --- a/interface/resources/qml/QmlWebWindow.qml +++ /dev/null @@ -1,63 +0,0 @@ - -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtWebEngine 1.1 -import QtWebChannel 1.0 -import QtWebSockets 1.0 - -import "qwebchannel.js" as WebChannel -import "controls" -import "styles" - -VrDialog { - id: root - HifiConstants { id: hifi } - title: "WebWindow" - resizable: true - contentImplicitWidth: clientArea.implicitWidth - contentImplicitHeight: clientArea.implicitHeight - backgroundColor: "#7f000000" - property url source: "about:blank" - - signal navigating(string url) - - Component.onCompleted: { - enabled = true - console.log("Web Window Created " + root); - webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { - console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); - }); - - webview.loadingChanged.connect(handleWebviewLoading) - } - - - function handleWebviewLoading(loadRequest) { - var HIFI_URL_PATTERN = /^hifi:\/\//; - if (WebEngineView.LoadStartedStatus == loadRequest.status) { - var newUrl = loadRequest.url.toString(); - if (newUrl.match(HIFI_URL_PATTERN)) { - root.navigating(newUrl); - } - } - } - - Item { - id: clientArea - implicitHeight: 600 - implicitWidth: 800 - x: root.clientX - y: root.clientY - width: root.clientWidth - height: root.clientHeight - - WebEngineView { - id: webview - url: root.source - anchors.fill: parent - profile: WebEngineProfile { - httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)" - } - } - } // item -} // dialog diff --git a/interface/resources/qml/TestMenu.qml b/interface/resources/qml/TestMenu.qml index fe8a26e234..4d109e6298 100644 --- a/interface/resources/qml/TestMenu.qml +++ b/interface/resources/qml/TestMenu.qml @@ -4,7 +4,116 @@ import Hifi 1.0 // Currently for testing a pure QML replacement menu Item { + Item { + objectName: "AllActions" + Action { + id: aboutApp + objectName: "HifiAction_" + MenuConstants.AboutApp + text: qsTr("About Interface") + } + + // + // File Menu + // + Action { + id: login + objectName: "HifiAction_" + MenuConstants.Login + text: qsTr("Login") + } + Action { + id: quit + objectName: "HifiAction_" + MenuConstants.Quit + text: qsTr("Quit") + //shortcut: StandardKey.Quit + shortcut: "Ctrl+Q" + } + + + // + // Edit menu + // + Action { + id: undo + text: "Undo" + shortcut: StandardKey.Undo + } + + Action { + id: redo + text: "Redo" + shortcut: StandardKey.Redo + } + + Action { + id: animations + objectName: "HifiAction_" + MenuConstants.Animations + text: qsTr("Animations...") + } + Action { + id: attachments + text: qsTr("Attachments...") + } + Action { + id: explode + text: qsTr("Explode on quit") + checkable: true + checked: true + } + Action { + id: freeze + text: qsTr("Freeze on quit") + checkable: true + checked: false + } + ExclusiveGroup { + Action { + id: visibleToEveryone + objectName: "HifiAction_" + MenuConstants.VisibleToEveryone + text: qsTr("Everyone") + checkable: true + checked: true + } + Action { + id: visibleToFriends + objectName: "HifiAction_" + MenuConstants.VisibleToFriends + text: qsTr("Friends") + checkable: true + } + Action { + id: visibleToNoOne + objectName: "HifiAction_" + MenuConstants.VisibleToNoOne + text: qsTr("No one") + checkable: true + } + } + } + Menu { objectName: "rootMenu"; + Menu { + title: "File" + MenuItem { action: login } + MenuItem { action: explode } + MenuItem { action: freeze } + MenuItem { action: quit } + } + Menu { + title: "Tools" + Menu { + title: "I Am Visible To" + MenuItem { action: visibleToEveryone } + MenuItem { action: visibleToFriends } + MenuItem { action: visibleToNoOne } + } + MenuItem { action: animations } + } + Menu { + title: "Long menu name top menu" + MenuItem { action: aboutApp } + } + Menu { + title: "Help" + MenuItem { action: aboutApp } + } } } diff --git a/interface/resources/qml/WebEntity.qml b/interface/resources/qml/WebEntity.qml index ae94105672..0eb943cac7 100644 --- a/interface/resources/qml/WebEntity.qml +++ b/interface/resources/qml/WebEntity.qml @@ -1,10 +1,10 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtWebEngine 1.1 +import QtWebKit 3.0 -WebEngineView { +WebView { id: root - anchors.fill: parent objectName: "webview" + anchors.fill: parent url: "about:blank" } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e437a05aa3..fa5565417d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -101,7 +101,6 @@ #include #include #include -#include #include "AnimDebugDraw.h" #include "AudioClient.h" @@ -363,17 +362,6 @@ Cube3DOverlay* _keyboardFocusHighlight{ nullptr }; int _keyboardFocusHighlightID{ -1 }; PluginContainer* _pluginContainer; - -// FIXME hack access to the internal share context for the Chromium helper -// Normally we'd want to use QWebEngine::initialize(), but we can't because -// our primary context is a QGLWidget, which can't easily be initialized to share -// from a QOpenGLContext. -// -// So instead we create a new offscreen context to share with the QGLWidget, -// and manually set THAT to be the shared context for the Chromium helper -OffscreenGLCanvas* _chromiumShareContext { nullptr }; -Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); - Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : QApplication(argc, argv), _dependencyManagerIsSetup(setupEssentials(argc, argv)), @@ -635,11 +623,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _glWidget->makeCurrent(); _glWidget->initializeGL(); - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->create(_glWidget->context()->contextHandle()); - _chromiumShareContext->makeCurrent(); - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); - _offscreenContext = new OffscreenGLCanvas(); _offscreenContext->create(_glWidget->context()->contextHandle()); _offscreenContext->makeCurrent(); diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 26a564e20b..bddc7d19ae 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -320,13 +320,10 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { void OffscreenQmlSurface::resize(const QSize& newSize) { if (!_renderer || !_renderer->_quickWindow) { - return; - } - - - QSize currentSize = _renderer->_quickWindow->geometry().size(); - if (newSize == currentSize) { - return; + QSize currentSize = _renderer->_quickWindow->geometry().size(); + if (newSize == currentSize) { + return; + } } _qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize); @@ -440,9 +437,7 @@ void OffscreenQmlSurface::updateQuick() { } if (_render) { - QMutexLocker lock(&(_renderer->_mutex)); _renderer->post(RENDER); - _renderer->_cond.wait(&(_renderer->_mutex)); _render = false; } diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index d66cbeb285..67315b0783 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -40,8 +40,8 @@ public: void create(QOpenGLContext* context); void resize(const QSize& size); QSize size() const; - Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { + QObject* load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); + QObject* load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { return load(QUrl(qmlSourceFile), f); } diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp index 6397d30e13..3e879df7af 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp @@ -39,11 +39,6 @@ void QOpenGLContextWrapper::doneCurrent() { _context->doneCurrent(); } -void QOpenGLContextWrapper::setShareContext(QOpenGLContext* otherContext) { - _context->setShareContext(otherContext); -} - bool isCurrentContext(QOpenGLContext* context) { return QOpenGLContext::currentContext() == context; -} - +} \ No newline at end of file diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.h b/libraries/gl/src/gl/QOpenGLContextWrapper.h index b736253213..832119162c 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.h +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.h @@ -25,12 +25,6 @@ public: void swapBuffers(QSurface* surface); bool makeCurrent(QSurface* surface); void doneCurrent(); - void setShareContext(QOpenGLContext* otherContext); - - QOpenGLContext* getContext() { - return _context; - } - private: QOpenGLContext* _context { nullptr }; diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index c1131765f7..3796abd92a 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME script-engine) setup_hifi_library(Gui Network Script WebSockets Widgets) -link_hifi_libraries(shared networking ui octree gpu procedural model model-networking recording avatars fbx entities controllers animation audio physics) +link_hifi_libraries(shared networking octree gpu procedural model model-networking recording avatars fbx entities controllers animation audio physics) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 5f39bee9fa..ded3db11e9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include @@ -351,8 +350,6 @@ void ScriptEngine::init() { qScriptRegisterSequenceMetaType >(this); qScriptRegisterSequenceMetaType >(this); - - registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor); QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor); globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index cc2382926f..140ca87d0d 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME ui) -setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebSockets XmlPatterns) +setup_hifi_library(OpenGL Network Qml Quick Script XmlPatterns) link_hifi_libraries(shared networking gl) diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp deleted file mode 100644 index d5cdc1fde9..0000000000 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ /dev/null @@ -1,240 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015-12-15 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "QmlWebWindowClass.h" - -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include "OffscreenUi.h" - -QWebSocketServer* QmlWebWindowClass::_webChannelServer { nullptr }; -static QWebChannel webChannel; -static const uint16_t WEB_CHANNEL_PORT = 51016; -static std::atomic nextWindowId; -static const char* const URL_PROPERTY = "source"; -static const char* const TITLE_PROPERTY = "title"; -static const QRegExp HIFI_URL_PATTERN { "^hifi://" }; - -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 QmlWebWindowClass::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())); - }); - } -} - -// Method called by Qt scripts to create a new web window in the overlay -QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { - QmlWebWindowClass* retVal { nullptr }; - const QString title = context->argument(0).toString(); - QString url = context->argument(1).toString(); - if (!url.startsWith("http") && !url.startsWith("file://")) { - url = QUrl::fromLocalFile(url).toString(); - } - const int width = std::max(100, std::min(1280, context->argument(2).toInt32()));; - const int height = std::max(100, std::min(720, context->argument(3).toInt32()));; - - // Build the event bridge and wrapper on the main thread - QMetaObject::invokeMethod(DependencyManager::get().data(), "load", Qt::BlockingQueuedConnection, - Q_ARG(const QString&, "QmlWebWindow.qml"), - Q_ARG(std::function, [&](QQmlContext* context, QObject* object) { - setupServer(); - retVal = new QmlWebWindowClass(object); - webChannel.registerObject(url.toLower(), retVal); - retVal->setTitle(title); - retVal->setURL(url); - retVal->setSize(width, height); - })); - connect(engine, &QScriptEngine::destroyed, retVal, &QmlWebWindowClass::deleteLater); - return engine->newQObject(retVal); -} - -QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) - : _windowId(++nextWindowId), _qmlWindow(qmlWindow) -{ - qDebug() << "Created window with ID " << _windowId; - Q_ASSERT(_qmlWindow); - Q_ASSERT(dynamic_cast(_qmlWindow)); - QObject::connect(_qmlWindow, SIGNAL(navigating(QString)), this, SLOT(handleNavigation(QString))); -} - -void QmlWebWindowClass::handleNavigation(const QString& url) { - DependencyManager::get()->handleLookupString(url); -} - -void QmlWebWindowClass::setVisible(bool visible) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setVisible", Qt::AutoConnection, Q_ARG(bool, visible)); - return; - } - - auto qmlWindow = asQuickItem(); - if (qmlWindow->isEnabled() != visible) { - qmlWindow->setEnabled(visible); - emit visibilityChanged(visible); - } -} - -QQuickItem* QmlWebWindowClass::asQuickItem() const { - return dynamic_cast(_qmlWindow); -} - -bool QmlWebWindowClass::isVisible() const { - if (QThread::currentThread() != thread()) { - bool result; - QMetaObject::invokeMethod(const_cast(this), "isVisible", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result)); - return result; - } - - return asQuickItem()->isEnabled(); -} - - -glm::vec2 QmlWebWindowClass::getPosition() const { - if (QThread::currentThread() != thread()) { - glm::vec2 result; - QMetaObject::invokeMethod(const_cast(this), "getPosition", Qt::BlockingQueuedConnection, Q_RETURN_ARG(glm::vec2, result)); - return result; - } - - return glm::vec2(asQuickItem()->x(), asQuickItem()->y()); -} - - -void QmlWebWindowClass::setPosition(const glm::vec2& position) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setPosition", Qt::QueuedConnection, Q_ARG(glm::vec2, position)); - return; - } - - asQuickItem()->setPosition(QPointF(position.x, position.y)); -} - -void QmlWebWindowClass::setPosition(int x, int y) { - setPosition(glm::vec2(x, y)); -} - -glm::vec2 QmlWebWindowClass::getSize() const { - if (QThread::currentThread() != thread()) { - glm::vec2 result; - QMetaObject::invokeMethod(const_cast(this), "getSize", Qt::BlockingQueuedConnection, Q_RETURN_ARG(glm::vec2, result)); - return result; - } - - return glm::vec2(asQuickItem()->width(), asQuickItem()->height()); -} - -void QmlWebWindowClass::setSize(const glm::vec2& size) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setSize", Qt::QueuedConnection, Q_ARG(glm::vec2, size)); - } - - asQuickItem()->setSize(QSizeF(size.x, size.y)); -} - -void QmlWebWindowClass::setSize(int width, int height) { - setSize(glm::vec2(width, height)); -} - -QString QmlWebWindowClass::getURL() const { - if (QThread::currentThread() != thread()) { - QString result; - QMetaObject::invokeMethod(const_cast(this), "getURL", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, result)); - return result; - } - return _qmlWindow->property(URL_PROPERTY).toString(); -} - -void QmlWebWindowClass::setURL(const QString& urlString) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setURL", Qt::QueuedConnection, Q_ARG(QString, urlString)); - } - _qmlWindow->setProperty(URL_PROPERTY, urlString); -} - - -void QmlWebWindowClass::setTitle(const QString& title) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setTitle", Qt::QueuedConnection, Q_ARG(QString, title)); - } - - _qmlWindow->setProperty(TITLE_PROPERTY, title); -} - -void QmlWebWindowClass::close() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); - } - _qmlWindow->setProperty("destroyOnInvisible", true); - _qmlWindow->setProperty("visible", false); - _qmlWindow->deleteLater(); -} - -void QmlWebWindowClass::hasClosed() { -} - -void QmlWebWindowClass::raise() { - // FIXME -} - -#include "QmlWebWindowClass.moc" diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h deleted file mode 100644 index 2b563e68ba..0000000000 --- a/libraries/ui/src/QmlWebWindowClass.h +++ /dev/null @@ -1,104 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015-12-15 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ui_QmlWebWindowClass_h -#define hifi_ui_QmlWebWindowClass_h - -#include -#include -#include -#include -#include - -class QScriptEngine; -class QScriptContext; -class QmlWebWindowClass; -class QWebSocketServer; -class QWebSocket; - -class QmlScriptEventBridge : public QObject { - Q_OBJECT -public: - QmlScriptEventBridge(const QmlWebWindowClass* 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 QmlWebWindowClass* _webWindow { nullptr }; - QWebSocket *_socket { nullptr }; -}; - -// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping -class QmlWebWindowClass : public QObject { - Q_OBJECT - Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT) - Q_PROPERTY(int windowId READ getWindowId CONSTANT) - Q_PROPERTY(QString url READ getURL CONSTANT) - Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition) - Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize) - Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged) - -public: - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); - QmlWebWindowClass(QObject* qmlWindow); - -public slots: - bool isVisible() const; - void setVisible(bool visible); - - glm::vec2 getPosition() const; - void setPosition(const glm::vec2& position); - void setPosition(int x, int y); - - glm::vec2 getSize() const; - void setSize(const glm::vec2& size); - void setSize(int width, int height); - - QString getURL() const; - void setURL(const QString& url); - - void setTitle(const QString& title); - - // Ugh.... do not want to do - Q_INVOKABLE void raise(); - Q_INVOKABLE void close(); - Q_INVOKABLE int getWindowId() const { return _windowId; }; - Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; }; - -signals: - void visibilityChanged(bool visible); // Tool window - void urlChanged(); - void moved(glm::vec2 position); - void resized(QSizeF size); - void closed(); - -private slots: - void hasClosed(); - void handleNavigation(const QString& url); - -private: - static void setupServer(); - static QWebSocketServer* _webChannelServer; - - QQuickItem* asQuickItem() const; - QmlScriptEventBridge* const _eventBridge { new QmlScriptEventBridge(this) }; - - // FIXME needs to be initialized in the ctor once we have support - // for tool window panes in QML - const bool _isToolWindow { false }; - const int _windowId; - QObject* const _qmlWindow; -}; - -#endif diff --git a/tests/ui/CMakeLists.txt b/tests/ui/CMakeLists.txt index f94a0b85c0..8fda001e14 100644 --- a/tests/ui/CMakeLists.txt +++ b/tests/ui/CMakeLists.txt @@ -2,32 +2,15 @@ set(TARGET_NAME "ui-test") # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Network OpenGL Qml Quick Script WebChannel WebEngine WebSockets) +setup_hifi_project(Widgets OpenGL Network Qml Quick Script) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") if (WIN32) target_link_libraries(${TARGET_NAME} wsock32.lib opengl32.lib Winmm.lib) - # Issue causes build failure unless we add this directory. - # See https://bugreports.qt.io/browse/QTBUG-43351 - add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine) endif() # link in the shared libraries link_hifi_libraries(shared networking gl gpu ui) -# copy the resources files beside the executable -add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${PROJECT_SOURCE_DIR}/qml" - $/qml -) - - -target_glew() - -if (WIN32) - set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/../../interface/resources/qml") -endif() - package_libraries_for_deployment() diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 59e7376f1b..18f62dc016 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -1,91 +1,41 @@ // -// Created by Bradley Austin Davis on 2015-04-22 -// Copyright 2013-2015 High Fidelity, Inc. +// main.cpp +// tests/render-utils/src +// +// Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include +#include "OffscreenUi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include - -#include -#include -#include #include +#include +#include #include -#include -#include -#include -#include -#include - -const QString& getResourcesDir() { - static QString dir; - if (dir.isEmpty()) { - QDir path(__FILE__); - path.cdUp(); - dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/"; - qDebug() << "Resources Path: " << dir; - } - return dir; -} - -const QString& getExamplesDir() { - static QString dir; - if (dir.isEmpty()) { - QDir path(__FILE__); - path.cdUp(); - dir = path.cleanPath(path.absoluteFilePath("../../../examples/")) + "/"; - qDebug() << "Resources Path: " << dir; - } - return dir; -} - -const QString& getInterfaceQmlDir() { - static QString dir; - if (dir.isEmpty()) { - dir = getResourcesDir() + "qml/"; - qDebug() << "Qml Path: " << dir; - } - return dir; -} - -const QString& getTestQmlDir() { - static QString dir; - if (dir.isEmpty()) { - QDir path(__FILE__); - path.cdUp(); - dir = path.cleanPath(path.absoluteFilePath("../")) + "/"; - qDebug() << "Qml Test Path: " << dir; - } - return dir; -} - +#include +#include "MessageDialog.h" +#include "VrMenu.h" +#include "InfoView.h" +#include class RateCounter { std::vector times; @@ -124,394 +74,142 @@ public: }; - - -extern QOpenGLContext* qt_gl_global_share_context(); - - -static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName) { - if (engine.hasUncaughtException()) { - const auto backtrace = engine.uncaughtExceptionBacktrace(); - const auto exception = engine.uncaughtException().toString(); - const auto line = QString::number(engine.uncaughtExceptionLineNumber()); - engine.clearExceptions(); - - auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); - if (!backtrace.empty()) { - static const auto lineSeparator = "\n "; - message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); - } - qWarning() << qPrintable(message); - return true; - } - return false; -} - -const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0f / 60.0f) * 1000 * 1000) + 0.5f); - -static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) { - QString message = ""; - for (int i = 0; i < context->argumentCount(); i++) { - if (i > 0) { - message += " "; - } - message += context->argument(i).toString(); - } - qDebug().noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline - - message = message.replace("\\", "\\\\") - .replace("\n", "\\n") - .replace("\r", "\\r") - .replace("'", "\\'"); - engine->evaluate("Script.print('" + message + "')"); - - return QScriptValue(); -} - -class ScriptEngine : public QScriptEngine { +class MenuConstants : public QObject{ Q_OBJECT + Q_ENUMS(Item) public: - void loadFile(const QString& scriptPath) { - if (_isRunning) { - return; - } - qDebug() << "Loading script from " << scriptPath; - _fileNameString = scriptPath; + enum Item { + RenderLookAtTargets, + }; + +public: + MenuConstants(QObject* parent = nullptr) : QObject(parent) { - QFile file(scriptPath); - if (file.exists()) { - file.open(QIODevice::ReadOnly); - _scriptContents = file.readAll(); - } else { - qFatal("Missing file "); - } - runInThread(); } - - Q_INVOKABLE void stop() { - if (!_isFinished) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "stop"); - return; - } - _isFinished = true; - if (_wantSignals) { - emit runningStateChanged(); - } - } - } - - Q_INVOKABLE void print(const QString& message) { - if (_wantSignals) { - emit printedMessage(message); - } - } - - Q_INVOKABLE QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot) { - // create the timer, add it to the map, and start it - QTimer* newTimer = new QTimer(this); - newTimer->setSingleShot(isSingleShot); - - connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired); - - // make sure the timer stops when the script does - connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop); - - _timerFunctionMap.insert(newTimer, function); - - newTimer->start(intervalMS); - return newTimer; - } - - Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS) { - return setupTimerWithInterval(function, intervalMS, false); - } - - Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS) { - return setupTimerWithInterval(function, timeoutMS, true); - } -private: - - void runInThread() { - QThread* workerThread = new QThread(); - connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); - connect(workerThread, &QThread::started, this, &ScriptEngine::run); - connect(workerThread, &QThread::finished, this, &ScriptEngine::deleteLater); - connect(this, &ScriptEngine::doneRunning, workerThread, &QThread::quit); - moveToThread(workerThread); - workerThread->start(); - } - - void init() { - _isInitialized = true; - registerMetaTypes(this); - registerGlobalObject("Script", this); - qScriptRegisterSequenceMetaType>(this); - qScriptRegisterSequenceMetaType>(this); - globalObject().setProperty("OverlayWebWindow", newFunction(QmlWebWindowClass::constructor)); - QScriptValue printConstructorValue = newFunction(debugPrint); - globalObject().setProperty("print", printConstructorValue); - } - - void timerFired() { - QTimer* callingTimer = reinterpret_cast(sender()); - QScriptValue timerFunction = _timerFunctionMap.value(callingTimer); - - if (!callingTimer->isActive()) { - // this timer is done, we can kill it - _timerFunctionMap.remove(callingTimer); - delete callingTimer; - } - - // call the associated JS function, if it exists - if (timerFunction.isValid()) { - timerFunction.call(); - } - } - - - void run() { - if (!_isInitialized) { - init(); - } - - _isRunning = true; - if (_wantSignals) { - emit runningStateChanged(); - } - - QScriptValue result = evaluate(_scriptContents, _fileNameString); - QElapsedTimer startTime; - startTime.start(); - - int thisFrame = 0; - - qint64 lastUpdate = usecTimestampNow(); - - while (!_isFinished) { - int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec - if (usecToSleep > 0) { - usleep(usecToSleep); - } - - if (_isFinished) { - break; - } - - QCoreApplication::processEvents(); - if (_isFinished) { - break; - } - - qint64 now = usecTimestampNow(); - float deltaTime = (float)(now - lastUpdate) / (float)USECS_PER_SECOND; - if (!_isFinished) { - if (_wantSignals) { - emit update(deltaTime); - } - } - lastUpdate = now; - - // Debug and clear exceptions - hadUncaughtExceptions(*this, _fileNameString); - } - - if (_wantSignals) { - emit scriptEnding(); - } - - if (_wantSignals) { - emit finished(_fileNameString, this); - } - - _isRunning = false; - - if (_wantSignals) { - emit runningStateChanged(); - emit doneRunning(); - } - } - - void registerGlobalObject(const QString& name, QObject* object) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "registerGlobalObject", - Q_ARG(const QString&, name), - Q_ARG(QObject*, object)); - return; - } - if (!globalObject().property(name).isValid()) { - if (object) { - QScriptValue value = newQObject(object); - globalObject().setProperty(name, value); - } else { - globalObject().setProperty(name, QScriptValue()); - } - } - } - - void registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "registerFunction", - Q_ARG(const QString&, name), - Q_ARG(QScriptEngine::FunctionSignature, functionSignature), - Q_ARG(int, numArguments)); - return; - } - QScriptValue scriptFun = newFunction(functionSignature, numArguments); - globalObject().setProperty(name, scriptFun); - } - - void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "registerFunction", - Q_ARG(const QString&, name), - Q_ARG(QScriptEngine::FunctionSignature, functionSignature), - Q_ARG(int, numArguments)); - return; - } - - QScriptValue object = globalObject().property(parent); - if (object.isValid()) { - QScriptValue scriptFun = newFunction(functionSignature, numArguments); - object.setProperty(name, scriptFun); - } - } - -signals: - void scriptLoaded(const QString& scriptFilename); - void errorLoadingScript(const QString& scriptFilename); - void update(float deltaTime); - void scriptEnding(); - void finished(const QString& fileNameString, ScriptEngine* engine); - void cleanupMenuItem(const QString& menuItemString); - void printedMessage(const QString& message); - void errorMessage(const QString& message); - void runningStateChanged(); - void evaluationFinished(QScriptValue result, bool isException); - void loadScript(const QString& scriptName, bool isUserLoaded); - void reloadScript(const QString& scriptName, bool isUserLoaded); - void doneRunning(); - - -private: - QString _scriptContents; - QString _fileNameString; - QString _parentURL; - bool _isInitialized { false }; - std::atomic _isFinished { false }; - std::atomic _isRunning { false }; - bool _wantSignals { true }; - QHash _timerFunctionMap; }; - - -ScriptEngine* loadScript(const QString& scriptFilename) { - ScriptEngine* scriptEngine = new ScriptEngine(); - scriptEngine->loadFile(scriptFilename); - return scriptEngine; +const QString& getResourcesDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/"; + qDebug() << "Resources Path: " << dir; + } + return dir; } -OffscreenGLCanvas* _chromiumShareContext { nullptr }; -Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); +const QString& getQmlDir() { + static QString dir; + if (dir.isEmpty()) { + dir = getResourcesDir() + "qml/"; + qDebug() << "Qml Path: " << dir; + } + return dir; +} +const QString& getTestQmlDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../")) + "/"; + qDebug() << "Qml Test Path: " << dir; + } + return dir; +} // Create a simple OpenGL window that renders text in various ways -class QTestWindow : public QWindow { +class QTestWindow : public QWindow, private QOpenGLFunctions { Q_OBJECT - QOpenGLContextWrapper* _context{ nullptr }; + QOpenGLContext* _context{ nullptr }; QSize _size; bool _altPressed{ false }; RateCounter fps; QTimer _timer; int testQmlTexture{ 0 }; - ProgramPtr _program; - ShapeWrapperPtr _plane; - QScriptEngine* _scriptEngine { nullptr }; public: QObject* rootMenu; QTestWindow() { - _scriptEngine = new ScriptEngine(); _timer.setInterval(1); - QObject::connect(&_timer, &QTimer::timeout, this, &QTestWindow::draw); + connect(&_timer, &QTimer::timeout, [=] { + draw(); + }); - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->create(); - _chromiumShareContext->makeCurrent(); - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + DependencyManager::set(); + setSurfaceType(QSurface::OpenGLSurface); - { - setSurfaceType(QSurface::OpenGLSurface); - QSurfaceFormat format = getDefaultOpenGLSurfaceFormat(); - setFormat(format); - _context = new QOpenGLContextWrapper(); - _context->setFormat(format); - _context->setShareContext(_chromiumShareContext->getContext()); - } + QSurfaceFormat format; + format.setDepthBufferSize(16); + format.setStencilBufferSize(8); + format.setVersion(4, 1); + format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile); + format.setOption(QSurfaceFormat::DebugContext); + setFormat(format); + _context = new QOpenGLContext; + _context->setFormat(format); if (!_context->create()) { qFatal("Could not create OpenGL context"); } show(); + makeCurrent(); + initializeOpenGLFunctions(); + + { + QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(this); + logger->initialize(); // initializes in the current context, i.e. ctx + logger->enableMessages(); + connect(logger, &QOpenGLDebugLogger::messageLogged, this, [&](const QOpenGLDebugMessage & debugMessage) { + qDebug() << debugMessage; + }); + // logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); + } + + qDebug() << (const char*)this->glGetString(GL_VERSION); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClearColor(0.2f, 0.2f, 0.2f, 1); + glDisable(GL_DEPTH_TEST); + + MessageDialog::registerType(); + VrMenu::registerType(); + InfoView::registerType(); + + + auto offscreenUi = DependencyManager::get(); + offscreenUi->create(_context); + connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) { + testQmlTexture = textureId; + }); makeCurrent(); - glewExperimental = true; - glewInit(); - glGetError(); - - using namespace oglplus; - Context::Enable(Capability::Blend); - Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha); - Context::Disable(Capability::DepthTest); - Context::Disable(Capability::CullFace); - Context::ClearColor(0.2f, 0.2f, 0.2f, 1); - - MessageDialog::registerType(); - InfoView::registerType(); - - auto offscreenUi = DependencyManager::set(); - { - offscreenUi->create(_context->getContext()); - offscreenUi->setProxyWindow(this); - - connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) { - testQmlTexture = textureId; - }); - - makeCurrent(); - } - - - auto primaryScreen = QGuiApplication::primaryScreen(); - auto targetScreen = primaryScreen; - auto screens = QGuiApplication::screens(); - if (screens.size() > 1) { - for (auto screen : screens) { - if (screen != targetScreen) { - targetScreen = screen; - break; - } - } - } - auto rect = targetScreen->availableGeometry(); - rect.setWidth(rect.width() * 0.8f); - rect.setHeight(rect.height() * 0.8f); - rect.moveTo(QPoint(20, 20)); + offscreenUi->setProxyWindow(this); + QDesktopWidget* desktop = QApplication::desktop(); + QRect rect = desktop->availableGeometry(desktop->screenCount() - 1); + int height = rect.height(); + //rect.setHeight(height / 2); + rect.setY(rect.y() + height / 2); setGeometry(rect); +// setFramePosition(QPoint(-1000, 0)); +// resize(QSize(800, 600)); #ifdef QML_CONTROL_GALLERY offscreenUi->setBaseUrl(QUrl::fromLocalFile(getTestQmlDir())); offscreenUi->load(QUrl("main.qml")); #else - offscreenUi->setBaseUrl(QUrl::fromLocalFile(getInterfaceQmlDir())); + offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); offscreenUi->load(QUrl("TestRoot.qml")); + offscreenUi->load(QUrl("TestMenu.qml")); + // Requires a root menu to have been loaded before it can load + VrMenu::load(); #endif installEventFilter(offscreenUi.data()); offscreenUi->resume(); @@ -529,35 +227,16 @@ private: } makeCurrent(); - auto error = glGetError(); - if (error != GL_NO_ERROR) { - qDebug() << "GL error in entering draw " << error; - } + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio()); - using namespace oglplus; - Context::Clear().ColorBuffer().DepthBuffer(); - ivec2 size(_size.width(), _size.height()); - size *= devicePixelRatio(); - size = glm::max(size, ivec2(100, 100)); - Context::Viewport(size.x, size.y); - if (!_program) { - _program = loadDefaultShader(); - _plane = loadPlane(_program); - } + renderQml(); - if (testQmlTexture > 0) { - glBindTexture(GL_TEXTURE_2D, testQmlTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - - _program->Bind(); - _plane->Use(); - _plane->Draw(); _context->swapBuffers(this); + glFinish(); fps.increment(); - if (fps.elapsed() >= 10.0f) { + if (fps.elapsed() >= 2.0f) { qDebug() << "FPS: " << fps.rate(); fps.reset(); } @@ -567,6 +246,8 @@ private: _context->makeCurrent(this); } + void renderQml(); + void resizeWindow(const QSize & size) { _size = size; DependencyManager::get()->resize(_size); @@ -588,13 +269,11 @@ protected: offscreenUi->load("Browser.qml"); } break; - - case Qt::Key_J: + case Qt::Key_L: if (event->modifiers() & Qt::CTRL) { - loadScript(getExamplesDir() + "tests/qmlWebTest.js"); + InfoView::show(getResourcesDir() + "html/interface-welcome.html", true); } break; - case Qt::Key_K: if (event->modifiers() & Qt::CTRL) { OffscreenUi::question("Message title", "Message contents", [](QMessageBox::Button b){ @@ -602,9 +281,22 @@ protected: }); } break; + case Qt::Key_J: + if (event->modifiers() & Qt::CTRL) { + auto offscreenUi = DependencyManager::get(); + rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); + QMetaObject::invokeMethod(rootMenu, "popup"); + } + break; } QWindow::keyPressEvent(event); } + QQmlContext* menuContext{ nullptr }; + void keyReleaseEvent(QKeyEvent *event) override { + if (_altPressed && Qt::Key_Alt == event->key()) { + VrMenu::toggle(); + } + } void moveEvent(QMoveEvent* event) override { static qreal oldPixelRatio = 0.0; @@ -616,26 +308,40 @@ protected: } }; +void QTestWindow::renderQml() { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + if (testQmlTexture > 0) { + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, testQmlTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + glBegin(GL_QUADS); + { + glTexCoord2f(0, 0); + glVertex2f(-1, -1); + glTexCoord2f(0, 1); + glVertex2f(-1, 1); + glTexCoord2f(1, 1); + glVertex2f(1, 1); + glTexCoord2f(1, 0); + glVertex2f(1, -1); + } + glEnd(); +} + + const char * LOG_FILTER_RULES = R"V0G0N( hifi.offscreen.focus.debug=false qt.quick.mouse.debug=false )V0G0N"; -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - QString logMessage = message; - -#ifdef Q_OS_WIN - if (!logMessage.isEmpty()) { - OutputDebugStringA(logMessage.toLocal8Bit().constData()); - OutputDebugStringA("\n"); - } -#endif -} - - int main(int argc, char** argv) { - QGuiApplication app(argc, argv); - qInstallMessageHandler(messageHandler); + QApplication app(argc, argv); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow window; app.exec();