diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index 7bb26a885a..c8c9b48699 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -27,6 +27,7 @@ Windows.ScrollingWindow { destroyOnCloseButton: false property alias source: webview.url property alias eventBridge: eventBridgeWrapper.eventBridge; + property alias scriptUrl: webview.userScriptUrl QtObject { id: eventBridgeWrapper @@ -71,6 +72,8 @@ Windows.ScrollingWindow { focus: true webChannel.registeredObjects: [eventBridgeWrapper] + property string userScriptUrl: "" + // Create a global EventBridge object for raiseAndLowerKeyboard. WebEngineScript { id: createGlobalEventBridge @@ -87,7 +90,15 @@ Windows.ScrollingWindow { worldId: WebEngineScript.MainWorld } - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] + // User script. + WebEngineScript { + id: userScript + sourceUrl: webview.userScriptUrl + injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. + worldId: WebEngineScript.MainWorld + } + + userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] } } } diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index 4efda1adaf..d799ff2db4 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -16,6 +16,7 @@ #include "OffscreenUi.h" static const char* const URL_PROPERTY = "source"; +static const char* const SCRIPT_PROPERTY = "scriptUrl"; // Method called by Qt scripts to create a new web window in the overlay QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { @@ -48,3 +49,15 @@ void QmlWebWindowClass::setURL(const QString& urlString) { } }); } + +void QmlWebWindowClass::setScriptUrl(const QString& script) { + DependencyManager::get()->executeOnUiThread([=] { + if (!_qmlWindow.isNull()) { + _qmlWindow->setProperty(SCRIPT_PROPERTY, script); + } + }); +} + +void QmlWebWindowClass::clearScriptUrl(const QString& script) { + setScriptUrl(""); +} diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h index dbfd8ebe18..3ed35217ca 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -22,6 +22,8 @@ public: public slots: QString getURL() const; void setURL(const QString& url); + void setScriptUrl(const QString& script); + void clearScriptUrl (const QString& script); signals: void urlChanged(); diff --git a/scripts/system/html/js/marketplaces.js b/scripts/system/html/js/marketplaces.js index 9deb7c5cf3..7b529184e3 100644 --- a/scripts/system/html/js/marketplaces.js +++ b/scripts/system/html/js/marketplaces.js @@ -1,12 +1,23 @@ +// +// marketplaces.js +// +// Copyright 2016 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 +// + function loaded() { bindExploreButtons(); } function bindExploreButtons() { - $('#exploreClaraMarketplace').on('click', function() { + $('#exploreClaraMarketplace').on('click', function () { + EventBridge.emitWebEvent("INJECT_CLARA"); window.location = "https://clara.io/library?gameCheck=true&public=true" }) - $('#exploreHifiMarketplace').on('click', function() { + $('#exploreHifiMarketplace').on('click', function () { + EventBridge.emitWebEvent("INJECT_HIFI"); window.location = "http://www.highfidelity.com/marketplace" }) } \ No newline at end of file diff --git a/scripts/system/html/js/marketplacesClara.js b/scripts/system/html/js/marketplacesClara.js new file mode 100644 index 0000000000..9eed23100e --- /dev/null +++ b/scripts/system/html/js/marketplacesClara.js @@ -0,0 +1,13 @@ +// +// marketplacesClara.js +// +// Created by David Rowe on 12 Nov 2016. +// Copyright 2016 High Fidelity, Inc. +// +// Injected into Clara.io marketplace Web page. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +console.log("Hello from marketplacesClara.js"); diff --git a/scripts/system/html/js/marketplacesDirectory.js b/scripts/system/html/js/marketplacesDirectory.js new file mode 100644 index 0000000000..b062735c99 --- /dev/null +++ b/scripts/system/html/js/marketplacesDirectory.js @@ -0,0 +1,13 @@ +// +// marketplacesDirectory.js +// +// Created by David Rowe on 12 Nov 2016. +// Copyright 2016 High Fidelity, Inc. +// +// Injected into marketplaces directory page. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +console.log("Hello from marketplacesDirectory.js"); diff --git a/scripts/system/html/js/marketplacesHiFi.js b/scripts/system/html/js/marketplacesHiFi.js new file mode 100644 index 0000000000..a74b9ce971 --- /dev/null +++ b/scripts/system/html/js/marketplacesHiFi.js @@ -0,0 +1,13 @@ +// +// marketplacesHiFi.js +// +// Created by David Rowe on 12 Nov 2016. +// Copyright 2016 High Fidelity, Inc. +// +// Injected into High Fidelity marketplace Web page. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +console.log("Hello from marketplacesHiFi.js"); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js new file mode 100644 index 0000000000..eae23cc65e --- /dev/null +++ b/scripts/system/marketplaces/marketplaces.js @@ -0,0 +1,142 @@ +// +// marketplaces.js +// +// Created by Eric Levin on 8 Jan 2016 +// Copyright 2016 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 +// + +(function() { // BEGIN LOCAL_SCOPE + +/* global WebTablet */ +Script.include("../libraries/WebTablet.js"); + +var toolIconUrl = Script.resolvePath("../assets/images/tools/"); + +var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html");; +var marketplaceWindow = new OverlayWebWindow({ + title: "Marketplace", + source: "about:blank", + width: 900, + height: 700, + visible: false +}); +marketplaceWindow.setScriptUrl(Script.resolvePath("../html/js/marketplacesDirectory.js")); + +marketplaceWindow.webEventReceived.connect(function (data) { + if (data === "INJECT_CLARA") { + marketplaceWindow.setScriptUrl(Script.resolvePath("../html/js/marketplacesClara.js")); + } + if (data === "INJECT_HIFI") { + marketplaceWindow.setScriptUrl(Script.resolvePath("../html/js/marketplacesHiFi.js")); + } +}); + +var toolHeight = 50; +var toolWidth = 50; +var TOOLBAR_MARGIN_Y = 0; +var marketplaceVisible = false; +var marketplaceWebTablet; + +// We persist clientOnly data in the .ini file, and reconsistitute it on restart. +// To keep things consistent, we pickle the tablet data in Settings, and kill any existing such on restart and domain change. +var persistenceKey = "io.highfidelity.lastDomainTablet"; + +function shouldShowWebTablet() { + var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); + var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); + var hasHydra = !!Controller.Hardware.Hydra; + return HMD.active && (leftPose.valid || rightPose.valid || hasHydra); +} + +function showMarketplace(marketplaceID) { + if (shouldShowWebTablet()) { + updateButtonState(true); + marketplaceWebTablet = new WebTablet(MARKETPLACES_URL, null, null, true); + Settings.setValue(persistenceKey, marketplaceWebTablet.pickle()); + } else { + var url = MARKETPLACES_URL; + if (marketplaceID) { + // $$$$$$$ TODO + url = url + "/items/" + marketplaceID; + } + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); + } + + marketplaceVisible = true; + UserActivityLogger.openedMarketplace(); +} + +function hideTablet(tablet) { + if (!tablet) { + return; + } + updateButtonState(false); + tablet.destroy(); + marketplaceWebTablet = null; + Settings.setValue(persistenceKey, ""); +} +function clearOldTablet() { // If there was a tablet from previous domain or session, kill it and let it be recreated + var tablet = WebTablet.unpickle(Settings.getValue(persistenceKey, "")); + hideTablet(tablet); +} +function hideMarketplace() { + if (marketplaceWindow.visible) { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); + } else if (marketplaceWebTablet) { + hideTablet(marketplaceWebTablet); + } + marketplaceVisible = false; +} + +function toggleMarketplace() { + if (marketplaceVisible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + +var browseExamplesButton = toolBar.addButton({ + imageURL: toolIconUrl + "market.svg", + objectName: "marketplace", + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 +}); + +function updateButtonState(visible) { + browseExamplesButton.writeProperty('buttonState', visible ? 0 : 1); + browseExamplesButton.writeProperty('defaultState', visible ? 0 : 1); + browseExamplesButton.writeProperty('hoverState', visible ? 2 : 3); +} +function onMarketplaceWindowVisibilityChanged() { + updateButtonState(marketplaceWindow.visible); + marketplaceVisible = marketplaceWindow.visible; +} + +function onClick() { + toggleMarketplace(); +} + +browseExamplesButton.clicked.connect(onClick); +marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged); + +clearOldTablet(); // Run once at startup, in case there's anything laying around from a crash. +// We could also optionally do something like Window.domainChanged.connect(function () {Script.setTimeout(clearOldTablet, 2000)}), +// but the HUD version stays around, so lets do the same. + +Script.scriptEnding.connect(function () { + toolBar.removeButton("marketplace"); + browseExamplesButton.clicked.disconnect(onClick); + marketplaceWindow.visibleChanged.disconnect(onMarketplaceWindowVisibilityChanged); +}); + +}()); // END LOCAL_SCOPE