From 5e82410371e5aa9012b02e2a381e4af5873ac9c4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 29 Oct 2016 21:31:27 +1300 Subject: [PATCH 001/101] Make Clara.io download more robust --- interface/resources/qml/Browser.qml | 3 +++ interface/resources/qml/Marketplaces.qml | 21 ++++++--------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 869c0556fb..27ebad4884 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -23,6 +23,8 @@ ScrollingWindow { property alias eventBridge: eventBridgeWrapper.eventBridge + signal loadingChanged(int status) + x: 100 y: 100 @@ -243,6 +245,7 @@ ScrollingWindow { if (loadRequest.status === WebEngineView.LoadSucceededStatus) { addressBar.text = loadRequest.url } + root.loadingChanged(loadRequest.status); } onIconChanged: { diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index 70a20286d3..18ac976e91 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -40,15 +40,6 @@ Rectangle { height: parent.height - statusBarHeight focus: true - Timer { - id: zipTimer - running: false - repeat: false - interval: 1500 - property var handler; - onTriggered: handler(); - } - Timer { id: alertTimer running: false @@ -113,13 +104,13 @@ Rectangle { request.openIn(newWindow.webView); if (File.isZippedFbx(desktop.currentUrl)) { runJavaScript(autoCancel); - zipTimer.handler = function() { - newWindow.destroy(); - } - zipTimer.start(); + newWindow.loadingChanged.connect(function(status) { + if (status > 0) { + newWindow.destroy(); // Download has kicked off so we can destroy the Web window. + } + }); } } - } Rectangle { @@ -164,4 +155,4 @@ Rectangle { } -} \ No newline at end of file +} From 97407e1cd5f52eda8b794c1b0fcc5affcb4d76a9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 29 Oct 2016 21:31:44 +1300 Subject: [PATCH 002/101] Canonical default browser URL --- interface/resources/qml/Browser.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 27ebad4884..65d0777d19 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -199,7 +199,7 @@ ScrollingWindow { WebView { id: webview - url: "https://highfidelity.com" + url: "https://highfidelity.com/" property alias eventBridgeWrapper: eventBridgeWrapper From 12747d26e082c6314f76047a25fd9fa7717e0e15 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 30 Oct 2016 13:58:17 +1300 Subject: [PATCH 003/101] Add parameter that enables automatically adding download model to world --- interface/resources/qml/Browser.qml | 2 +- interface/resources/qml/hifi/Desktop.qml | 6 ++++-- interface/src/Application.cpp | 16 +++++++++++++++- interface/src/Application.h | 2 ++ .../script-engine/src/FileScriptingInterface.cpp | 4 ++-- .../script-engine/src/FileScriptingInterface.h | 4 ++-- 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 65d0777d19..2097dc7148 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -257,7 +257,7 @@ ScrollingWindow { } Component.onCompleted: { - desktop.initWebviewProfileHandlers(webview.profile) + desktop.initWebviewProfileHandlers(webview.profile, false); } } diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 7f1fbcb174..a1848f51cf 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -88,10 +88,12 @@ OriginalDesktop.Desktop { property string currentUrl: "" property string adaptedPath: "" property string tempDir: "" + property bool autoAdd: false - function initWebviewProfileHandlers(profile) { + function initWebviewProfileHandlers(profile, auto) { console.log("The webview url in desktop is: " + currentUrl); if (webViewProfileSetup) return; + autoAdd = auto; webViewProfileSetup = true; profile.downloadRequested.connect(function(download){ @@ -109,7 +111,7 @@ OriginalDesktop.Desktop { profile.downloadFinished.connect(function(download){ if (download.state === WebEngineDownloadItem.DownloadCompleted) { - File.runUnzip(download.path, currentUrl); + File.runUnzip(download.path, currentUrl, autoAdd); } else { console.log("The download was corrupted, state: " + download.state); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 425fccf8fb..9158a9085e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1769,7 +1769,7 @@ void Application::initializeUi() { rootContext->setContextProperty("Entities", DependencyManager::get().data()); FileScriptingInterface* fileDownload = new FileScriptingInterface(engine); rootContext->setContextProperty("File", fileDownload); - connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::showAssetServerWidget); + connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::handleUnzip); rootContext->setContextProperty("MyAvatar", getMyAvatar().get()); rootContext->setContextProperty("Messages", DependencyManager::get().data()); rootContext->setContextProperty("Recording", DependencyManager::get().data()); @@ -5330,6 +5330,20 @@ void Application::showAssetServerWidget(QString filePath) { startUpload(nullptr, nullptr); } +void Application::addAssetToWorld(QString filePath) { + // Automatically upload and add asset to world rather than proceeding manually per showAssetServerWidget(). + + // TODO +} + +void Application::handleUnzip(QString filePath, bool autoAdd) { + if (autoAdd && !filePath.isEmpty()) { + addAssetToWorld(filePath); + } else { + showAssetServerWidget(filePath); + } +} + void Application::packageModel() { ModelPackager::package(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 23cb58c19b..bd5f111588 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -306,6 +306,8 @@ public slots: void toggleLogDialog(); void toggleRunningScriptsWidget() const; Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); + void addAssetToWorld(QString filePath); + void handleUnzip(QString filePath = "", bool autoAdd = false); void handleLocalServerConnection() const; void readArgumentsFromLocalSocket() const; diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index ad6a3cdf6f..c6a16e9d4c 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -31,7 +31,7 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent // nothing for now } -void FileScriptingInterface::runUnzip(QString path, QUrl url) { +void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd) { qDebug() << "Url that was downloaded: " + url.toString(); qDebug() << "Path where download is saved: " + path; QString fileName = "/" + path.section("/", -1); @@ -47,7 +47,7 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url) { if (file != "") { qDebug() << "Object file to upload: " + file; QUrl url = QUrl::fromLocalFile(file); - emit unzipSuccess(url.toString()); + emit unzipSuccess(url.toString(), autoAdd); } else { qDebug() << "unzip failed"; } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index d9a100b293..900aec2b0b 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -28,11 +28,11 @@ public slots: bool isZipped(QUrl url); bool isClaraLink(QUrl url); QString convertUrlToPath(QUrl url); - void runUnzip(QString path, QUrl url); + void runUnzip(QString path, QUrl url, bool autoAdd); QString getTempDir(); signals: - void unzipSuccess(QString url); + void unzipSuccess(QString url, bool autoAdd); private: bool isTempDir(QString tempDir); From e0ef24a460ab0d4094c0acd7a555c28d9061166a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 30 Oct 2016 15:35:20 +1300 Subject: [PATCH 004/101] Set auto adding for Clara downloads --- interface/resources/qml/Browser.qml | 6 +++++- interface/resources/qml/Marketplaces.qml | 1 + interface/resources/qml/hifi/Desktop.qml | 9 +++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 2097dc7148..bd98e42709 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -46,6 +46,10 @@ ScrollingWindow { hidePermissionsBar(); } + function setAutoAdd(auto) { + desktop.setAutoAdd(auto); + } + Item { id:item width: pane.contentWidth @@ -257,7 +261,7 @@ ScrollingWindow { } Component.onCompleted: { - desktop.initWebviewProfileHandlers(webview.profile, false); + desktop.initWebviewProfileHandlers(webview.profile); } } diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index 18ac976e91..f3236dcd31 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -103,6 +103,7 @@ Rectangle { var newWindow = component.createObject(desktop); request.openIn(newWindow.webView); if (File.isZippedFbx(desktop.currentUrl)) { + newWindow.setAutoAdd(true); runJavaScript(autoCancel); newWindow.loadingChanged.connect(function(status) { if (status > 0) { diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index a1848f51cf..c396df9d4d 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -90,10 +90,9 @@ OriginalDesktop.Desktop { property string tempDir: "" property bool autoAdd: false - function initWebviewProfileHandlers(profile, auto) { + function initWebviewProfileHandlers(profile) { console.log("The webview url in desktop is: " + currentUrl); if (webViewProfileSetup) return; - autoAdd = auto; webViewProfileSetup = true; profile.downloadRequested.connect(function(download){ @@ -103,6 +102,7 @@ OriginalDesktop.Desktop { console.log("Temp dir created: " + tempDir); download.path = tempDir + "/" + adaptedPath; console.log("Path where object should download: " + download.path); + console.log("Auto add: " + autoAdd); download.accept(); if (download.state === WebEngineDownloadItem.DownloadInterrupted) { console.log("download failed to complete"); @@ -115,9 +115,14 @@ OriginalDesktop.Desktop { } else { console.log("The download was corrupted, state: " + download.state); } + autoAdd = false; }) } + function setAutoAdd(auto) { + autoAdd = auto; + } + // Create or fetch a toolbar with the given name function getToolbar(name) { var result = toolbars[name]; From 098200a5dd7e31bb178d00a58f1333a613147b6c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 30 Oct 2016 15:50:41 +1300 Subject: [PATCH 005/101] Hide brief flash of browser window used for download --- interface/resources/qml/Marketplaces.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index f3236dcd31..c94fb0559e 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -101,6 +101,14 @@ Rectangle { onNewViewRequested: { var component = Qt.createComponent("Browser.qml"); var newWindow = component.createObject(desktop); + + // Hide brief flash of browser window behind marketplace window. + newWindow.x = x; + newWindow.y = y; + newWindow.z = 0; + newWindow.width = 0; + newWindow.height = 0; + request.openIn(newWindow.webView); if (File.isZippedFbx(desktop.currentUrl)) { newWindow.setAutoAdd(true); From 8ab56fe8711f3a30a717f049de3524825dc5dd99 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 31 Oct 2016 16:02:20 +1300 Subject: [PATCH 006/101] Automatically download Clara model to ATP and add to world --- interface/src/Application.cpp | 69 ++++++++++++++++++++++++++++++++++- interface/src/Application.h | 3 ++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9158a9085e..757a9bc80a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,7 @@ #include #include #include +#include #include #include #include @@ -5331,9 +5333,72 @@ void Application::showAssetServerWidget(QString filePath) { } void Application::addAssetToWorld(QString filePath) { - // Automatically upload and add asset to world rather than proceeding manually per showAssetServerWidget(). + // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). - // TODO + if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { + qCDebug(interfaceapp) << "Error downloading asset: Do not have permissions to write to asset server"; + OffscreenUi::warning("Error Downloading Asset", "Do not have permissions to write to asset server"); + return; + } + + QString path = QUrl(filePath).toLocalFile(); + QString mapping = path.right(path.length() - path.lastIndexOf("/")); + + addAssetToWorldUpload(path, mapping); +} + +void Application::addAssetToWorldUpload(QString path, QString mapping) { + auto upload = DependencyManager::get()->createUpload(path); + QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable { + if (upload->getError() != AssetUpload::NoError) { + QString errorInfo = "Could not upload asset to asset server"; + qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + OffscreenUi::warning("Error Downloading Asset", errorInfo); + } else { + addAssetToWorldSetMapping(mapping, hash); + } + upload->deleteLater(); + }); + + upload->start(); +} + +void Application::addAssetToWorldSetMapping(QString mapping, QString hash) { + auto request = DependencyManager::get()->createSetMappingRequest(mapping, hash); + connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable { + if (request->getError() != SetMappingRequest::NoError) { + QString errorInfo = "Could not set asset mapping"; + qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + OffscreenUi::warning("Error Downloading Asset", errorInfo); + } else { + addAssetToWorldAddEntity(mapping); + } + request->deleteLater(); + }); + + request->start(); +} + +void Application::addAssetToWorldAddEntity(QString mapping) { + EntityItemProperties properties; + properties.setType(EntityTypes::Model); + properties.setName(mapping.right(mapping.length() - 1)); + properties.setModelURL("atp:" + mapping); + properties.setShapeType(SHAPE_TYPE_STATIC_MESH); + properties.setDynamic(false); + properties.setPosition(getMyAvatar()->getPosition() + getMyAvatar()->getOrientation() * glm::vec3(0.0f, 0.0f, -2.0f)); + properties.setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); + auto result = DependencyManager::get()->addEntity(properties); + + if (result == QUuid()) { + QString errorInfo = "Could not add downloaded asset " + mapping + " to world"; + qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + OffscreenUi::warning("Error Downloading Asset", errorInfo); + } else { + QString successInfo = "Downloaded asset " + mapping + " added to world"; + qCDebug(interfaceapp) << "Downloading asset completed: " + successInfo; + OffscreenUi::information("Downloading Asset Completed", successInfo); + } } void Application::handleUnzip(QString filePath, bool autoAdd) { diff --git a/interface/src/Application.h b/interface/src/Application.h index bd5f111588..0ba91adb55 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -307,6 +307,9 @@ public slots: void toggleRunningScriptsWidget() const; Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); void addAssetToWorld(QString filePath); + void addAssetToWorldUpload(QString path, QString mapping); + void addAssetToWorldSetMapping(QString mapping, QString hash); + void addAssetToWorldAddEntity(QString mapping); void handleUnzip(QString filePath = "", bool autoAdd = false); void handleLocalServerConnection() const; From 3ca2c1907511768a32ea6987eba24ce39f62c527 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 1 Nov 2016 12:31:35 +1300 Subject: [PATCH 007/101] Delete temporary directory after content has been used --- interface/src/Application.cpp | 9 +++++++++ libraries/script-engine/src/FileScriptingInterface.cpp | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 757a9bc80a..cd5fd08ea3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5357,6 +5357,15 @@ void Application::addAssetToWorldUpload(QString path, QString mapping) { } else { addAssetToWorldSetMapping(mapping, hash); } + + // Remove temporary directory created by Clara.io market place download. + int index = path.lastIndexOf("/model_repo/"); + if (index > 0) { + QString tempDir = path.left(index); + qCDebug(interfaceapp) << "Removing temporary directory at: " + tempDir; + QDir(tempDir).removeRecursively(); + } + upload->deleteLater(); }); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index c6a16e9d4c..cd9d92da8b 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -51,8 +51,6 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd) { } else { qDebug() << "unzip failed"; } - qDebug() << "Removing temporary directory at: " + tempDir; - QDir(tempDir).removeRecursively(); } // fix to check that we are only referring to a temporary directory From cee8b66ce99fa5dac2ee32f249d756a4ce1e523a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 1 Nov 2016 13:26:05 +1300 Subject: [PATCH 008/101] Handle duplicate Clara download asset names --- interface/src/Application.cpp | 27 ++++++++++++++++++++++++++- interface/src/Application.h | 1 + 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cd5fd08ea3..3b4b457e14 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5344,7 +5344,32 @@ void Application::addAssetToWorld(QString filePath) { QString path = QUrl(filePath).toLocalFile(); QString mapping = path.right(path.length() - path.lastIndexOf("/")); - addAssetToWorldUpload(path, mapping); + addAssetToWorldWithNewMapping(path, mapping, 0); +} + +void Application::addAssetToWorldWithNewMapping(QString path, QString mapping, int copy) { + auto request = DependencyManager::get()->createGetMappingRequest(mapping); + QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { + const int MAX_COPY_COUNT = 100; // Limit number of duplicate assets; recursion guard. + if (request->getError() == GetMappingRequest::NotFound) { + addAssetToWorldUpload(path, mapping); + } else if (copy < MAX_COPY_COUNT - 1) { + if (copy > 0) { + mapping = mapping.remove(mapping.lastIndexOf("-"), QString::number(copy).length() + 1); + } + copy++; + mapping = mapping.insert(mapping.lastIndexOf("."), "-" + QString::number(copy)); + addAssetToWorldWithNewMapping(path, mapping, copy); + } else { + QString errorInfo = "Too many copies of asset name: " + + mapping.left(mapping.length() - QString::number(copy).length() - 1); + qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + OffscreenUi::warning("Error Downloading Asset", errorInfo); + } + request->deleteLater(); + }); + + request->start(); } void Application::addAssetToWorldUpload(QString path, QString mapping) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 0ba91adb55..4c31e8d121 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -307,6 +307,7 @@ public slots: void toggleRunningScriptsWidget() const; Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); void addAssetToWorld(QString filePath); + void addAssetToWorldWithNewMapping(QString path, QString mapping, int copy); void addAssetToWorldUpload(QString path, QString mapping); void addAssetToWorldSetMapping(QString mapping, QString hash); void addAssetToWorldAddEntity(QString mapping); From 93beaa81c0d0f50876c1344f5c8fad4803863623 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 1 Nov 2016 13:27:05 +1300 Subject: [PATCH 009/101] Code tidying --- interface/resources/qml/Marketplaces.qml | 5 ++--- interface/src/Application.cpp | 11 ++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index c94fb0559e..5bee3a84d9 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -25,6 +25,7 @@ Rectangle { HifiConstants { id: hifi } id: marketplace anchors.fill: parent + property var marketplacesUrl: "../../scripts/system/html/marketplaces.html" property int statusBarHeight: 50 property int statusMargin: 50 @@ -85,7 +86,6 @@ Rectangle { statusIcon.text = hifi.glyphs.alert; runJavaScript(notFbxHandler, displayErrorStatus()); } - } onLoadingChanged: { @@ -138,7 +138,7 @@ Rectangle { width: 150 text: "See all markets" onClicked: { - webview.url = "../../scripts/system/html/marketplaces.html"; + webview.url = marketplacesUrl; statusLabel.text = standardMessage; } } @@ -163,5 +163,4 @@ Rectangle { } } - } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3b4b457e14..bc7820879c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5336,8 +5336,9 @@ void Application::addAssetToWorld(QString filePath) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { - qCDebug(interfaceapp) << "Error downloading asset: Do not have permissions to write to asset server"; - OffscreenUi::warning("Error Downloading Asset", "Do not have permissions to write to asset server"); + QString errorInfo = "Do not have permissions to write to asset server."; + qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + OffscreenUi::warning("Error Downloading Asset", errorInfo); return; } @@ -5376,7 +5377,7 @@ void Application::addAssetToWorldUpload(QString path, QString mapping) { auto upload = DependencyManager::get()->createUpload(path); QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable { if (upload->getError() != AssetUpload::NoError) { - QString errorInfo = "Could not upload asset to asset server"; + QString errorInfo = "Could not upload asset to asset server."; qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; OffscreenUi::warning("Error Downloading Asset", errorInfo); } else { @@ -5401,7 +5402,7 @@ void Application::addAssetToWorldSetMapping(QString mapping, QString hash) { auto request = DependencyManager::get()->createSetMappingRequest(mapping, hash); connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable { if (request->getError() != SetMappingRequest::NoError) { - QString errorInfo = "Could not set asset mapping"; + QString errorInfo = "Could not set asset mapping."; qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; OffscreenUi::warning("Error Downloading Asset", errorInfo); } else { @@ -5425,7 +5426,7 @@ void Application::addAssetToWorldAddEntity(QString mapping) { auto result = DependencyManager::get()->addEntity(properties); if (result == QUuid()) { - QString errorInfo = "Could not add downloaded asset " + mapping + " to world"; + QString errorInfo = "Could not add downloaded asset " + mapping + " to world."; qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; OffscreenUi::warning("Error Downloading Asset", errorInfo); } else { From 667a7776955a6624993987e0e485d1a7351c8aba Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 1 Nov 2016 23:43:15 +1300 Subject: [PATCH 010/101] Use Clara.io API for FBX downloads --- interface/resources/qml/Marketplaces.qml | 21 ++++++++++++++++----- interface/resources/qml/hifi/Desktop.qml | 8 ++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index 5bee3a84d9..167a456e06 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -56,7 +56,7 @@ Rectangle { property var simpleDownload: 'var element = $("a.download-file"); element.removeClass("download-file"); element.removeAttr("download");' - + function displayErrorStatus() { alertTimer.handler = function() { statusLabel.text = claraMessage; @@ -69,12 +69,23 @@ Rectangle { property var notFbxHandler: 'var element = $("a.btn.btn-primary.viewer-button.download-file") element.click();' - // this code is for removing other file types from Clara.io's download options - //property var checkFileType: "$('[data-extension]:not([data-extension=\"fbx\"])').parent().remove()" + // Replace Clara FBX download link action with Cara API action. + property string replaceFBXDownload: 'var element = $("a[data-extension=\'fbx\']:first"); + element.unbind("click"); + element.bind("click", function(event) { + console.log("Initiate Clara.io FBX file download"); + window.open("https://clara.io/api/scenes/{uuid}/export/fbx?fbxVersion=7.4&fbxEmbedTextures=true¢erScene=true&alignSceneGound=true"); + return false; + });' onLinkHovered: { desktop.currentUrl = hoveredUrl; - //runJavaScript(checkFileType, function(){console.log("Remove filetypes JS injection");}); + + if (desktop.isClaraFBXZipDownload(desktop.currentUrl)) { + var doReplaceFBXDownload = replaceFBXDownload.replace("{uuid}", desktop.currentUrl.slice(desktop.currentUrl.lastIndexOf("/") + 1, -1)); + runJavaScript(doReplaceFBXDownload); + } + if (File.isZippedFbx(desktop.currentUrl)) { runJavaScript(simpleDownload, function(){console.log("Download JS injection");}); return; @@ -110,7 +121,7 @@ Rectangle { newWindow.height = 0; request.openIn(newWindow.webView); - if (File.isZippedFbx(desktop.currentUrl)) { + if (desktop.isClaraFBXZipDownload(desktop.currentUrl)) { newWindow.setAutoAdd(true); runJavaScript(autoCancel); newWindow.loadingChanged.connect(function(status) { diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index c396df9d4d..04a7c404f4 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -90,6 +90,11 @@ OriginalDesktop.Desktop { property string tempDir: "" property bool autoAdd: false + function isClaraFBXZipDownload(url) { + // Clara.io FBX file: Original URL that is ovrridden in JavaScript to download using API. + return url.indexOf("clara.io/view/") !== -1 && url.slice(-1) === "#"; + } + function initWebviewProfileHandlers(profile) { console.log("The webview url in desktop is: " + currentUrl); if (webViewProfileSetup) return; @@ -101,6 +106,9 @@ OriginalDesktop.Desktop { tempDir = File.getTempDir(); console.log("Temp dir created: " + tempDir); download.path = tempDir + "/" + adaptedPath; + if (isClaraFBXZipDownload(currentUrl)) { + download.path += "fbx.zip"; + } console.log("Path where object should download: " + download.path); console.log("Auto add: " + autoAdd); download.accept(); From 35448b6c0993f64c1096ef860d06125593c8250d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 2 Nov 2016 00:05:10 +1300 Subject: [PATCH 011/101] Update Clara.io instructions --- scripts/system/html/marketplaces.html | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index ea85c7ec62..5e838cf2cb 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -19,13 +19,9 @@
-

- Marketplaces -

+

Marketplaces

-

- You can bring content into High Fidelity forom anywhere you want. Here are a few places that support direct import of content right now. If you'd like to suggest a Market to include here, let us know. -

+

You can bring content into High Fidelity from anywhere you want. Here are a few places that support direct import of content right now. If you'd like to suggest a Market to include here, let us know.

@@ -34,7 +30,7 @@

This is the default High Fidelity marketplace. Viewing and downloading content from here is fully supported in Interface.

-
+

@@ -47,19 +43,13 @@
  1. Create an account here or log in as an existing user.
  2. Choose a model from the list and click Download -> Autodesk FBX.
  3. -
  4. After the file processes, click Download.
  5. -
  6. Add the model to your asset server, then find it from the list and choose Add To World.
- +
-
- - - \ No newline at end of file From 0c6ed67e3b62670f1a5dda34b6fa07fa1067d405 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 2 Nov 2016 00:05:30 +1300 Subject: [PATCH 012/101] Tidying --- scripts/system/html/js/marketplaces.js | 4 ++-- scripts/system/marketplaces/clara.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/js/marketplaces.js b/scripts/system/html/js/marketplaces.js index a1b3847b3c..9deb7c5cf3 100644 --- a/scripts/system/html/js/marketplaces.js +++ b/scripts/system/html/js/marketplaces.js @@ -1,10 +1,10 @@ function loaded() { -bindExploreButtons(); + bindExploreButtons(); } function bindExploreButtons() { $('#exploreClaraMarketplace').on('click', function() { - window.location = "https://clara.io/library?public=true" + window.location = "https://clara.io/library?gameCheck=true&public=true" }) $('#exploreHifiMarketplace').on('click', function() { window.location = "http://www.highfidelity.com/marketplace" diff --git a/scripts/system/marketplaces/clara.js b/scripts/system/marketplaces/clara.js index a04400497f..ab1e5131bc 100644 --- a/scripts/system/marketplaces/clara.js +++ b/scripts/system/marketplaces/clara.js @@ -19,7 +19,7 @@ var qml = Script.resolvePath("../../../resources/qml/Marketplaces.qml") var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; var marketplaceWindow = new OverlayWindow({ - title: "Marketplace", + title: "Marketplaces", source: qml, width: 1000, height: 900, From 10490a9fadd170533c9d58b2f21e48e6ae0b247a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 2 Nov 2016 10:45:00 +1300 Subject: [PATCH 013/101] Default collision shap to hull per submesh instead of static --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bc7820879c..a11909c948 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5419,7 +5419,7 @@ void Application::addAssetToWorldAddEntity(QString mapping) { properties.setType(EntityTypes::Model); properties.setName(mapping.right(mapping.length() - 1)); properties.setModelURL("atp:" + mapping); - properties.setShapeType(SHAPE_TYPE_STATIC_MESH); + properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); properties.setDynamic(false); properties.setPosition(getMyAvatar()->getPosition() + getMyAvatar()->getOrientation() * glm::vec3(0.0f, 0.0f, -2.0f)); properties.setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); From 41c7f714e20e66aaff4cbc41d7efc1dead2ef9b7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 2 Nov 2016 16:49:45 +1300 Subject: [PATCH 014/101] Add keyboard control to marketplaces window --- interface/resources/qml/Marketplaces.qml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index 167a456e06..f05f7e11a0 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -33,12 +33,20 @@ Rectangle { property string claraMessage: "Choose a model and click Download -> Autodesk FBX." property string claraError: "High Fidelity only supports Autodesk FBX models." + property bool keyboardEnabled: true + property bool keyboardRaised: true + property bool punctuationMode: false + + onVisibleChanged: { + keyboardEnabled = HMD.active; + } + Controls.BaseWebView { id: webview url: marketplacesUrl anchors.top: marketplace.top width: parent.width - height: parent.height - statusBarHeight + height: parent.height - statusBarHeight - keyboard.height focus: true Timer { @@ -136,7 +144,7 @@ Rectangle { Rectangle { id: statusBar anchors.top: webview.bottom - anchors.bottom: parent.bottom + anchors.bottom: keyboard.top anchors.left: parent.left anchors.right: parent.right color: hifi.colors.blueHighlight @@ -172,6 +180,17 @@ Rectangle { color: hifi.colors.white size: hifi.fontSizes.tableHeadingIcon } + } + Controls.Keyboard { + id: keyboard + enabled: keyboardEnabled + raised: keyboardEnabled && keyboardRaised + numeric: punctuationMode + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } } } From c578cd4b6da416551736b74c21bb649ff10fd02f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 2 Nov 2016 17:06:40 +1300 Subject: [PATCH 015/101] Inject keyboard raising and lowering script into marketplaces window --- interface/resources/qml/Marketplaces.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index f05f7e11a0..f83f99d8fb 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -49,6 +49,16 @@ Rectangle { height: parent.height - statusBarHeight - keyboard.height focus: true + // Detect when may want to raise and lower keyboard. + WebEngineScript { + id: raiseAndLowerKeyboard + injectionPoint: WebEngineScript.Deferred + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" + worldId: WebEngineScript.MainWorld + } + + userScripts: [ raiseAndLowerKeyboard ] + Timer { id: alertTimer running: false From 8ccf645d1c324f4fbdf4e2d6f2687947fd4b1ccd Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 2 Nov 2016 21:41:06 +1300 Subject: [PATCH 016/101] Make script event bridge available to OverlayWindow --- libraries/ui/src/QmlWebWindowClass.cpp | 45 ------------------------- libraries/ui/src/QmlWebWindowClass.h | 8 ----- libraries/ui/src/QmlWindowClass.cpp | 46 ++++++++++++++++++++++++++ libraries/ui/src/QmlWindowClass.h | 12 +++++++ 4 files changed, 58 insertions(+), 53 deletions(-) diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index 2130e84220..4efda1adaf 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -31,51 +31,6 @@ QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngi return engine->newQObject(retVal); } -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 { - // Special case to handle raising and lowering the virtual keyboard. - const QString RAISE_KEYBOARD = "_RAISE_KEYBOARD"; - const QString RAISE_KEYBOARD_NUMERIC = "_RAISE_KEYBOARD_NUMERIC"; - const QString LOWER_KEYBOARD = "_LOWER_KEYBOARD"; - QString messageString = webMessage.type() == QVariant::String ? webMessage.toString() : ""; - if (messageString.left(RAISE_KEYBOARD.length()) == RAISE_KEYBOARD) { - setKeyboardRaised(asQuickItem(), true, messageString == RAISE_KEYBOARD_NUMERIC); - } else if (messageString == LOWER_KEYBOARD) { - setKeyboardRaised(asQuickItem(), false); - } else { - emit webEventReceived(webMessage); - } - } -} - -void QmlWebWindowClass::setKeyboardRaised(QObject* object, bool raised, bool numeric) { - if (!object) { - return; - } - - QQuickItem* item = dynamic_cast(object); - while (item) { - if (item->property("keyboardRaised").isValid()) { - if (item->property("punctuationMode").isValid()) { - item->setProperty("punctuationMode", QVariant(numeric)); - } - item->setProperty("keyboardRaised", QVariant(raised)); - return; - } - item = dynamic_cast(item->parentItem()); - } -} - QString QmlWebWindowClass::getURL() const { QVariant result = DependencyManager::get()->returnFromUiThread([&]()->QVariant { if (_qmlWindow.isNull()) { diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h index 731afb3acb..dbfd8ebe18 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -23,19 +23,11 @@ public slots: QString getURL() const; void setURL(const QString& url); - void emitScriptEvent(const QVariant& scriptMessage); - void emitWebEvent(const QVariant& webMessage); - signals: void urlChanged(); - void scriptEventReceived(const QVariant& message); - void webEventReceived(const QVariant& message); protected: QString qmlSource() const override { return "QmlWebWindow.qml"; } - -private: - void setKeyboardRaised(QObject* object, bool raised, bool numeric = false); }; #endif diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 554ae7d8c2..c60e4fa698 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -150,6 +150,52 @@ void QmlWindowClass::sendToQml(const QVariant& message) { QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); } + +void QmlWindowClass::emitScriptEvent(const QVariant& scriptMessage) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, scriptMessage)); + } else { + emit scriptEventReceived(scriptMessage); + } +} + +void QmlWindowClass::emitWebEvent(const QVariant& webMessage) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage)); + } else { + // Special case to handle raising and lowering the virtual keyboard. + const QString RAISE_KEYBOARD = "_RAISE_KEYBOARD"; + const QString RAISE_KEYBOARD_NUMERIC = "_RAISE_KEYBOARD_NUMERIC"; + const QString LOWER_KEYBOARD = "_LOWER_KEYBOARD"; + QString messageString = webMessage.type() == QVariant::String ? webMessage.toString() : ""; + if (messageString.left(RAISE_KEYBOARD.length()) == RAISE_KEYBOARD) { + setKeyboardRaised(asQuickItem(), true, messageString == RAISE_KEYBOARD_NUMERIC); + } else if (messageString == LOWER_KEYBOARD) { + setKeyboardRaised(asQuickItem(), false); + } else { + emit webEventReceived(webMessage); + } + } +} + +void QmlWindowClass::setKeyboardRaised(QObject* object, bool raised, bool numeric) { + if (!object) { + return; + } + + QQuickItem* item = dynamic_cast(object); + while (item) { + if (item->property("keyboardRaised").isValid()) { + if (item->property("punctuationMode").isValid()) { + item->setProperty("punctuationMode", QVariant(numeric)); + } + item->setProperty("keyboardRaised", QVariant(raised)); + return; + } + item = dynamic_cast(item->parentItem()); + } +} + QmlWindowClass::~QmlWindowClass() { close(); } diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index 07cf736334..a6f59104fd 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -51,6 +51,10 @@ public slots: // Scripts can use this to send a message to the QML object void sendToQml(const QVariant& message); + // QmlWindow content may include WebView requiring EventBridge. + void emitScriptEvent(const QVariant& scriptMessage); + void emitWebEvent(const QVariant& webMessage); + signals: void visibleChanged(); void positionChanged(); @@ -61,6 +65,10 @@ signals: // Scripts can connect to this signal to receive messages from the QML object void fromQml(const QVariant& message); + // QmlWindow content may include WebView requiring EventBridge. + void scriptEventReceived(const QVariant& message); + void webEventReceived(const QVariant& message); + protected slots: void hasMoved(QVector2D); void hasClosed(); @@ -81,6 +89,10 @@ protected: bool _toolWindow { false }; QPointer _qmlWindow; QString _source; + +private: + // QmlWindow content may include WebView requiring EventBridge. + void setKeyboardRaised(QObject* object, bool raised, bool numeric = false); }; #endif From de5e02310bb43933d2836615d7f442fbe93572b9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 3 Nov 2016 09:00:40 +1300 Subject: [PATCH 017/101] Provide keyboard in marketplaces window --- interface/resources/qml/Marketplaces.qml | 19 ++++++++++++------- interface/resources/qml/QmlWindow.qml | 18 ++++++++++++++---- scripts/system/html/marketplaces.html | 2 -- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index f83f99d8fb..573c00c237 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -11,9 +11,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtWebChannel 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 import QtWebSockets 1.0 -import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel import "controls" import "controls-uit" as Controls @@ -33,10 +32,6 @@ Rectangle { property string claraMessage: "Choose a model and click Download -> Autodesk FBX." property string claraError: "High Fidelity only supports Autodesk FBX models." - property bool keyboardEnabled: true - property bool keyboardRaised: true - property bool punctuationMode: false - onVisibleChanged: { keyboardEnabled = HMD.active; } @@ -49,6 +44,16 @@ Rectangle { height: parent.height - statusBarHeight - keyboard.height focus: true + webChannel.registeredObjects: [eventBridgeWrapper] + + // Create a global EventBridge object for raiseAndLowerKeyboard. + WebEngineScript { + id: createGlobalEventBridge + sourceCode: eventBridgeJavaScriptToInject + injectionPoint: WebEngineScript.DocumentCreation + worldId: WebEngineScript.MainWorld + } + // Detect when may want to raise and lower keyboard. WebEngineScript { id: raiseAndLowerKeyboard @@ -57,7 +62,7 @@ Rectangle { worldId: WebEngineScript.MainWorld } - userScripts: [ raiseAndLowerKeyboard ] + userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] Timer { id: alertTimer diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index ac6ae31c7f..ac18d36ce6 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -2,9 +2,8 @@ import QtQuick 2.3 import QtQuick.Controls 1.4 import QtWebChannel 1.0 -import QtWebEngine 1.1 +import QtWebEngine 1.2 import QtWebSockets 1.0 -import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel import "windows" as Windows import "controls" @@ -23,10 +22,22 @@ Windows.Window { // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer destroyOnCloseButton: false property var source; - property var eventBridge; property var component; property var dynamicContent; + // Keyboard control properties in case needed by QML content. + property bool keyboardEnabled: false + property bool keyboardRaised: false + property bool punctuationMode: false + + // JavaScript event bridge object in case QML content includes Web content. + property alias eventBridge: eventBridgeWrapper.eventBridge; + + QtObject { + id: eventBridgeWrapper + WebChannel.id: "eventBridgeWrapper" + property var eventBridge; + } onSourceChanged: { if (dynamicContent) { @@ -72,7 +83,6 @@ Windows.Window { } } - Item { id: contentHolder anchors.fill: parent diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 5e838cf2cb..1f93fd47aa 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -13,8 +13,6 @@ - - From 0c479605536c7521607f0dde1e1f35912fca0366 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 3 Nov 2016 09:01:28 +1300 Subject: [PATCH 018/101] Don't raise keyboard for non-text HTML input fields --- interface/resources/html/raiseAndLowerKeyboard.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/resources/html/raiseAndLowerKeyboard.js b/interface/resources/html/raiseAndLowerKeyboard.js index 3897c9ff3f..0fb5529052 100644 --- a/interface/resources/html/raiseAndLowerKeyboard.js +++ b/interface/resources/html/raiseAndLowerKeyboard.js @@ -17,7 +17,10 @@ var KEYBOARD_HEIGHT = 200; function shouldRaiseKeyboard() { - if (document.activeElement.nodeName === "INPUT" || document.activeElement.nodeName === "TEXTAREA") { + var nodeName = document.activeElement.nodeName; + var nodeType = document.activeElement.type; + if (nodeName === "INPUT" && (nodeType === "text" || nodeType === "number") + || document.activeElement.nodeName === "TEXTAREA") { return true; } else { // check for contenteditable attribute From d1e54a92171fe024b944b3aa2780d215ed271286 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 4 Nov 2016 10:49:16 +1300 Subject: [PATCH 019/101] Fix Clara download failing if move mouse after initiating download --- interface/resources/qml/Marketplaces.qml | 4 ++-- interface/resources/qml/hifi/Desktop.qml | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index 573c00c237..9780de04f2 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -96,7 +96,7 @@ Rectangle { property string replaceFBXDownload: 'var element = $("a[data-extension=\'fbx\']:first"); element.unbind("click"); element.bind("click", function(event) { - console.log("Initiate Clara.io FBX file download"); + console.log("Initiate Clara.io FBX file download for {uuid}"); window.open("https://clara.io/api/scenes/{uuid}/export/fbx?fbxVersion=7.4&fbxEmbedTextures=true¢erScene=true&alignSceneGound=true"); return false; });' @@ -105,7 +105,7 @@ Rectangle { desktop.currentUrl = hoveredUrl; if (desktop.isClaraFBXZipDownload(desktop.currentUrl)) { - var doReplaceFBXDownload = replaceFBXDownload.replace("{uuid}", desktop.currentUrl.slice(desktop.currentUrl.lastIndexOf("/") + 1, -1)); + var doReplaceFBXDownload = replaceFBXDownload.replace(/{uuid}/g, desktop.currentUrl.slice(desktop.currentUrl.lastIndexOf("/") + 1, -1)); runJavaScript(doReplaceFBXDownload); } diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 04a7c404f4..b5d26b71d4 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -86,6 +86,7 @@ OriginalDesktop.Desktop { // Accept a download through the webview property bool webViewProfileSetup: false property string currentUrl: "" + property string downloadUrl: "" property string adaptedPath: "" property string tempDir: "" property bool autoAdd: false @@ -97,16 +98,17 @@ OriginalDesktop.Desktop { function initWebviewProfileHandlers(profile) { console.log("The webview url in desktop is: " + currentUrl); + downloadUrl = currentUrl; if (webViewProfileSetup) return; webViewProfileSetup = true; profile.downloadRequested.connect(function(download){ console.log("Download start: " + download.state); - adaptedPath = File.convertUrlToPath(currentUrl); + adaptedPath = File.convertUrlToPath(downloadUrl); tempDir = File.getTempDir(); console.log("Temp dir created: " + tempDir); download.path = tempDir + "/" + adaptedPath; - if (isClaraFBXZipDownload(currentUrl)) { + if (isClaraFBXZipDownload(downloadUrl)) { download.path += "fbx.zip"; } console.log("Path where object should download: " + download.path); @@ -119,7 +121,7 @@ OriginalDesktop.Desktop { profile.downloadFinished.connect(function(download){ if (download.state === WebEngineDownloadItem.DownloadCompleted) { - File.runUnzip(download.path, currentUrl, autoAdd); + File.runUnzip(download.path, downloadUrl, autoAdd); } else { console.log("The download was corrupted, state: " + download.state); } From 090065ee346b1c8a3c1daf60e6db33a19c9bfb0e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 8 Nov 2016 13:05:35 +1300 Subject: [PATCH 020/101] Add user feedback on download process --- interface/resources/qml/Marketplaces.qml | 11 +++++ interface/src/Application.cpp | 58 +++++++++++++++++++++--- interface/src/Application.h | 7 +++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index 9780de04f2..fa4811ff5b 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -97,6 +97,7 @@ Rectangle { element.unbind("click"); element.bind("click", function(event) { console.log("Initiate Clara.io FBX file download for {uuid}"); + EventBridge.emitWebEvent("CLARA.IO DOWNLOAD"); window.open("https://clara.io/api/scenes/{uuid}/export/fbx?fbxVersion=7.4&fbxEmbedTextures=true¢erScene=true&alignSceneGound=true"); return false; });' @@ -154,6 +155,16 @@ Rectangle { }); } } + + function onWebEventReceived(event) { + if (event === "CLARA.IO DOWNLOAD") { + ApplicationInterface.addAssetToWorldInitiate(); + } + } + + Component.onCompleted: { + eventBridge.webEventReceived.connect(onWebEventReceived); + } } Rectangle { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 21ffea7e5c..36d85dcfe3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5348,13 +5348,41 @@ void Application::showAssetServerWidget(QString filePath) { startUpload(nullptr, nullptr); } +void Application::addAssetToWorldInitiate() { + qCDebug(interfaceapp) << "Start downloading asset file"; + + if (!_addAssetToWorldMessageBox) { + _addAssetToWorldMessageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, + "Downloading Asset", "Preparing asset for download", QMessageBox::Cancel, QMessageBox::NoButton); + } + + connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed())); +} + +void Application::onAssetToWorldMessageBoxClosed() { + disconnect(_addAssetToWorldMessageBox); + _addAssetToWorldMessageBox = nullptr; +} + +void Application::addAssetToWorldError(QString errorText) { + _addAssetToWorldMessageBox->setProperty("title", "Error Downloading Asset"); + _addAssetToWorldMessageBox->setProperty("icon", OffscreenUi::ICON_CRITICAL); + _addAssetToWorldMessageBox->setProperty("text", errorText); +} + void Application::addAssetToWorld(QString filePath) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). + if (!_addAssetToWorldMessageBox) { + return; + } + + _addAssetToWorldMessageBox->setProperty("text", "Downloading asset file"); + if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { QString errorInfo = "Do not have permissions to write to asset server."; qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; - OffscreenUi::warning("Error Downloading Asset", errorInfo); + addAssetToWorldError(errorInfo); return; } @@ -5365,6 +5393,10 @@ void Application::addAssetToWorld(QString filePath) { } void Application::addAssetToWorldWithNewMapping(QString path, QString mapping, int copy) { + if (!_addAssetToWorldMessageBox) { + return; + } + auto request = DependencyManager::get()->createGetMappingRequest(mapping); QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { const int MAX_COPY_COUNT = 100; // Limit number of duplicate assets; recursion guard. @@ -5381,7 +5413,7 @@ void Application::addAssetToWorldWithNewMapping(QString path, QString mapping, i QString errorInfo = "Too many copies of asset name: " + mapping.left(mapping.length() - QString::number(copy).length() - 1); qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; - OffscreenUi::warning("Error Downloading Asset", errorInfo); + addAssetToWorldError(errorInfo); } request->deleteLater(); }); @@ -5390,12 +5422,16 @@ void Application::addAssetToWorldWithNewMapping(QString path, QString mapping, i } void Application::addAssetToWorldUpload(QString path, QString mapping) { + if (!_addAssetToWorldMessageBox) { + return; + } + auto upload = DependencyManager::get()->createUpload(path); QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable { if (upload->getError() != AssetUpload::NoError) { QString errorInfo = "Could not upload asset to asset server."; qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; - OffscreenUi::warning("Error Downloading Asset", errorInfo); + addAssetToWorldError(errorInfo); } else { addAssetToWorldSetMapping(mapping, hash); } @@ -5415,12 +5451,16 @@ void Application::addAssetToWorldUpload(QString path, QString mapping) { } void Application::addAssetToWorldSetMapping(QString mapping, QString hash) { + if (!_addAssetToWorldMessageBox) { + return; + } + auto request = DependencyManager::get()->createSetMappingRequest(mapping, hash); connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable { if (request->getError() != SetMappingRequest::NoError) { QString errorInfo = "Could not set asset mapping."; qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; - OffscreenUi::warning("Error Downloading Asset", errorInfo); + addAssetToWorldError(errorInfo); } else { addAssetToWorldAddEntity(mapping); } @@ -5431,6 +5471,10 @@ void Application::addAssetToWorldSetMapping(QString mapping, QString hash) { } void Application::addAssetToWorldAddEntity(QString mapping) { + if (!_addAssetToWorldMessageBox) { + return; + } + EntityItemProperties properties; properties.setType(EntityTypes::Model); properties.setName(mapping.right(mapping.length() - 1)); @@ -5444,11 +5488,13 @@ void Application::addAssetToWorldAddEntity(QString mapping) { if (result == QUuid()) { QString errorInfo = "Could not add downloaded asset " + mapping + " to world."; qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; - OffscreenUi::warning("Error Downloading Asset", errorInfo); + addAssetToWorldError(errorInfo); } else { QString successInfo = "Downloaded asset " + mapping + " added to world"; qCDebug(interfaceapp) << "Downloading asset completed: " + successInfo; - OffscreenUi::information("Downloading Asset Completed", successInfo); + _addAssetToWorldMessageBox->setProperty("text", "Downloading asset completed"); + _addAssetToWorldMessageBox->setProperty("buttons", QMessageBox::Ok); + _addAssetToWorldMessageBox->setProperty("defaultButton", QMessageBox::Ok); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 7c1d3eab34..d3862c07b9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -309,6 +310,7 @@ public slots: void toggleLogDialog(); void toggleRunningScriptsWidget() const; Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); + Q_INVOKABLE void addAssetToWorldInitiate(); void addAssetToWorld(QString filePath); void addAssetToWorldWithNewMapping(QString path, QString mapping, int copy); void addAssetToWorldUpload(QString path, QString mapping); @@ -398,6 +400,8 @@ private slots: void updateDisplayMode(); void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo); + void onAssetToWorldMessageBoxClosed(); + private: static void initDisplay(); void init(); @@ -615,6 +619,9 @@ private: model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ; gpu::TexturePointer _defaultSkyboxTexture; gpu::TexturePointer _defaultSkyboxAmbientTexture; + + QQuickItem* _addAssetToWorldMessageBox{ nullptr }; + void Application::addAssetToWorldError(QString errorText); }; From afd2a900a39258b2e0b02af8c1de61cd8cefe198 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 8 Nov 2016 13:22:50 +1300 Subject: [PATCH 021/101] Improve error handling for case that don't have permissions --- interface/src/Application.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 36d85dcfe3..deb703e6e6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5400,8 +5400,14 @@ void Application::addAssetToWorldWithNewMapping(QString path, QString mapping, i auto request = DependencyManager::get()->createGetMappingRequest(mapping); QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { const int MAX_COPY_COUNT = 100; // Limit number of duplicate assets; recursion guard. - if (request->getError() == GetMappingRequest::NotFound) { + auto result = request->getError(); + if (result == GetMappingRequest::NotFound) { addAssetToWorldUpload(path, mapping); + } else if (result != GetMappingRequest::NoError) { + QString errorInfo = "Could not map asset name: " + + mapping.left(mapping.length() - QString::number(copy).length() - 1); + qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + addAssetToWorldError(errorInfo); } else if (copy < MAX_COPY_COUNT - 1) { if (copy > 0) { mapping = mapping.remove(mapping.lastIndexOf("-"), QString::number(copy).length() + 1); From 8cceda1cfe53fd1995db28ef40dc3f2815a3a0f2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 8 Nov 2016 13:23:44 +1300 Subject: [PATCH 022/101] Tidy UI text --- interface/src/Application.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index deb703e6e6..0a5e5e1789 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5353,7 +5353,7 @@ void Application::addAssetToWorldInitiate() { if (!_addAssetToWorldMessageBox) { _addAssetToWorldMessageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, - "Downloading Asset", "Preparing asset for download", QMessageBox::Cancel, QMessageBox::NoButton); + "Downloading Asset", "Preparing asset for download.", QMessageBox::Cancel, QMessageBox::NoButton); } connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed())); @@ -5377,7 +5377,7 @@ void Application::addAssetToWorld(QString filePath) { return; } - _addAssetToWorldMessageBox->setProperty("text", "Downloading asset file"); + _addAssetToWorldMessageBox->setProperty("text", "Downloading asset file."); if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { QString errorInfo = "Do not have permissions to write to asset server."; @@ -5496,9 +5496,9 @@ void Application::addAssetToWorldAddEntity(QString mapping) { qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; addAssetToWorldError(errorInfo); } else { - QString successInfo = "Downloaded asset " + mapping + " added to world"; + QString successInfo = "Downloaded asset " + mapping + " added to world."; qCDebug(interfaceapp) << "Downloading asset completed: " + successInfo; - _addAssetToWorldMessageBox->setProperty("text", "Downloading asset completed"); + _addAssetToWorldMessageBox->setProperty("text", successInfo); _addAssetToWorldMessageBox->setProperty("buttons", QMessageBox::Ok); _addAssetToWorldMessageBox->setProperty("defaultButton", QMessageBox::Ok); } From 76de7bf03caf3f15a0d78871c7e7f5989c63ee03 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 8 Nov 2016 13:53:37 +1300 Subject: [PATCH 023/101] Fix typo --- interface/src/Application.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index d3862c07b9..ac74390ac8 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -621,7 +621,7 @@ private: gpu::TexturePointer _defaultSkyboxAmbientTexture; QQuickItem* _addAssetToWorldMessageBox{ nullptr }; - void Application::addAssetToWorldError(QString errorText); + void addAssetToWorldError(QString errorText); }; From 84795439ea7c2d6210884f1c209a3d0990787f52 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 10 Nov 2016 16:29:48 +1300 Subject: [PATCH 024/101] Display Clara.io download information --- interface/resources/qml/Marketplaces.qml | 24 +++++++----------------- interface/resources/qml/hifi/Desktop.qml | 8 -------- interface/src/Application.cpp | 4 +--- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index fa4811ff5b..d9752c6918 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -76,10 +76,6 @@ Rectangle { property var autoCancel: 'var element = $("a.btn.cancel"); element.click();' - property var simpleDownload: 'var element = $("a.download-file"); - element.removeClass("download-file"); - element.removeAttr("download");' - function displayErrorStatus() { alertTimer.handler = function() { statusLabel.text = claraMessage; @@ -92,26 +88,20 @@ Rectangle { property var notFbxHandler: 'var element = $("a.btn.btn-primary.viewer-button.download-file") element.click();' - // Replace Clara FBX download link action with Cara API action. - property string replaceFBXDownload: 'var element = $("a[data-extension=\'fbx\']:first"); - element.unbind("click"); + // Overload Clara FBX download link action. + property string replaceFBXDownload: 'var element = $("a.download-file"); + element.removeClass("download-file"); + element.removeAttr("download"); element.bind("click", function(event) { - console.log("Initiate Clara.io FBX file download for {uuid}"); EventBridge.emitWebEvent("CLARA.IO DOWNLOAD"); - window.open("https://clara.io/api/scenes/{uuid}/export/fbx?fbxVersion=7.4&fbxEmbedTextures=true¢erScene=true&alignSceneGound=true"); - return false; + console.log("Clara.io FBX file download initiated for {uuid}"); });' onLinkHovered: { desktop.currentUrl = hoveredUrl; - if (desktop.isClaraFBXZipDownload(desktop.currentUrl)) { - var doReplaceFBXDownload = replaceFBXDownload.replace(/{uuid}/g, desktop.currentUrl.slice(desktop.currentUrl.lastIndexOf("/") + 1, -1)); - runJavaScript(doReplaceFBXDownload); - } - if (File.isZippedFbx(desktop.currentUrl)) { - runJavaScript(simpleDownload, function(){console.log("Download JS injection");}); + runJavaScript(replaceFBXDownload, function(){console.log("Download JS injection");}); return; } @@ -145,7 +135,7 @@ Rectangle { newWindow.height = 0; request.openIn(newWindow.webView); - if (desktop.isClaraFBXZipDownload(desktop.currentUrl)) { + if (File.isZippedFbx(desktop.currentUrl)) { newWindow.setAutoAdd(true); runJavaScript(autoCancel); newWindow.loadingChanged.connect(function(status) { diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 6ab69e9893..03484d212f 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -105,11 +105,6 @@ OriginalDesktop.Desktop { property string tempDir: "" property bool autoAdd: false - function isClaraFBXZipDownload(url) { - // Clara.io FBX file: Original URL that is ovrridden in JavaScript to download using API. - return url.indexOf("clara.io/view/") !== -1 && url.slice(-1) === "#"; - } - function initWebviewProfileHandlers(profile) { console.log("The webview url in desktop is: " + currentUrl); downloadUrl = currentUrl; @@ -122,9 +117,6 @@ OriginalDesktop.Desktop { tempDir = File.getTempDir(); console.log("Temp dir created: " + tempDir); download.path = tempDir + "/" + adaptedPath; - if (isClaraFBXZipDownload(downloadUrl)) { - download.path += "fbx.zip"; - } console.log("Path where object should download: " + download.path); console.log("Auto add: " + autoAdd); download.accept(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b17697c021..3dcdf4c695 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5379,7 +5379,7 @@ void Application::addAssetToWorldInitiate() { if (!_addAssetToWorldMessageBox) { _addAssetToWorldMessageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, - "Downloading Asset", "Preparing asset for download.", QMessageBox::Cancel, QMessageBox::NoButton); + "Downloading Asset", "Downloading asset file.", QMessageBox::Cancel, QMessageBox::NoButton); } connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed())); @@ -5403,8 +5403,6 @@ void Application::addAssetToWorld(QString filePath) { return; } - _addAssetToWorldMessageBox->setProperty("text", "Downloading asset file."); - if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { QString errorInfo = "Do not have permissions to write to asset server."; qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; From 5503adac3e5957553991b22665929d6e22d084ee Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 10 Nov 2016 17:55:12 +1300 Subject: [PATCH 025/101] Display "image" option for each Clara marketplace item --- interface/resources/qml/Marketplaces.qml | 32 ++++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index d9752c6918..e6bb7a2265 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -73,9 +73,6 @@ Rectangle { onTriggered: handler(); } - property var autoCancel: 'var element = $("a.btn.cancel"); - element.click();' - function displayErrorStatus() { alertTimer.handler = function() { statusLabel.text = claraMessage; @@ -85,8 +82,19 @@ Rectangle { alertTimer.start(); } - property var notFbxHandler: 'var element = $("a.btn.btn-primary.viewer-button.download-file") - element.click();' + // In library page: + // - Open each item in "image" view. + property string updateLibraryPage: 'if ($) { + $(document).ready(function() { + var elements = $("a.thumbnail"); + for (var i = 0, length = elements.length; i < length; i++) { + var value = elements[i].getAttribute("href"); + if (value.slice(-6) !== "/image") { + elements[i].setAttribute("href", value + "/image"); + } + } + }); + }'; // Overload Clara FBX download link action. property string replaceFBXDownload: 'var element = $("a.download-file"); @@ -97,6 +105,20 @@ Rectangle { console.log("Clara.io FBX file download initiated for {uuid}"); });' + property var notFbxHandler: 'var element = $("a.btn.btn-primary.viewer-button.download-file"); + element.click();' + + property var autoCancel: 'var element = $("a.btn.cancel"); + element.click();' + + onUrlChanged: { + var location = url.toString(); + if (location.indexOf("clara.io/library") !== -1) { + // Catalog page. + runJavaScript(updateLibraryPage, function() { console.log("Library link JS injection"); }); + } + } + onLinkHovered: { desktop.currentUrl = hoveredUrl; From 492e98c31e002453f25186321a1c898847b41676 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 10 Nov 2016 18:20:36 +1300 Subject: [PATCH 026/101] Fix up library link from Clara item page --- interface/resources/qml/Marketplaces.qml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index e6bb7a2265..4da6774494 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -96,6 +96,17 @@ Rectangle { }); }'; + // In item page: + // - Fix up library link URL. + property string updateItemPage: 'if ($) { + var element = $("a[href=\'/library\']")[0]; + var parameters = "?gameCheck=true&public=true"; + var href = element.getAttribute("href"); + if (href.slice(-parameters.length) !== parameters) { + element.setAttribute("href", href + parameters); + } + }'; + // Overload Clara FBX download link action. property string replaceFBXDownload: 'var element = $("a.download-file"); element.removeClass("download-file"); @@ -117,6 +128,11 @@ Rectangle { // Catalog page. runJavaScript(updateLibraryPage, function() { console.log("Library link JS injection"); }); } + + if (location.indexOf("clara.io/view/") !== -1) { + // Item page. + runJavaScript(updateItemPage, function() { console.log("Item link JS injection"); }); + } } onLinkHovered: { From 1fe5e451bf4210debd6ea4dc76378349f5a5cb7c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 10 Nov 2016 20:28:41 +1300 Subject: [PATCH 027/101] Customize Clara item menu --- interface/resources/qml/Marketplaces.qml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index 4da6774494..bd7a89c8de 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -98,13 +98,24 @@ Rectangle { // In item page: // - Fix up library link URL. + // - Reuse FBX download button as HiFi download button. + // - Remove "Edit Online", "Get Embed Code", and other download buttons. property string updateItemPage: 'if ($) { - var element = $("a[href=\'/library\']")[0]; + var element = $("a[href^=\'/library\']")[0]; var parameters = "?gameCheck=true&public=true"; var href = element.getAttribute("href"); if (href.slice(-parameters.length) !== parameters) { element.setAttribute("href", href + parameters); } + var buttons = $("a.embed-button").parent("div"); + if (buttons.length > 0) { + var downloadFBX = buttons.find("a[data-extension=\'fbx\']")[0]; + var firstButton = buttons.children(":first-child")[0]; + buttons[0].insertBefore(downloadFBX, firstButton); + downloadFBX.setAttribute("class", "btn btn-primary download"); + downloadFBX.innerHTML = " Download to High Fidelity"; + buttons.children(":nth-child(2), .btn-group , .embed-button").each(function () { this.remove(); }); + } }'; // Overload Clara FBX download link action. From 1caca601f2cd40feb31e8b51fed60dae5266e696 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 10 Nov 2016 20:39:29 +1300 Subject: [PATCH 028/101] Update user instructions --- scripts/system/html/marketplaces.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 1f93fd47aa..970af1158f 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -40,7 +40,7 @@

Clara.io has thousands of models available for importing into High Fidelity. Follow these steps for the best experience:

  1. Create an account here or log in as an existing user.
  2. -
  3. Choose a model from the list and click Download -> Autodesk FBX.
  4. +
  5. Choose a model from the list and click “Download to High Fidelity”.
From 975eea870279816b1c15c67402ce482e8d50fb03 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 11 Nov 2016 12:04:05 +1300 Subject: [PATCH 029/101] Automatically continue once Zip file is ready --- interface/resources/qml/Marketplaces.qml | 43 ++++++++++++++---------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml index bd7a89c8de..ea5515b05f 100644 --- a/interface/resources/qml/Marketplaces.qml +++ b/interface/resources/qml/Marketplaces.qml @@ -110,23 +110,38 @@ Rectangle { var buttons = $("a.embed-button").parent("div"); if (buttons.length > 0) { var downloadFBX = buttons.find("a[data-extension=\'fbx\']")[0]; + downloadFBX.addEventListener("click", startAutoDownload); var firstButton = buttons.children(":first-child")[0]; buttons[0].insertBefore(downloadFBX, firstButton); downloadFBX.setAttribute("class", "btn btn-primary download"); downloadFBX.innerHTML = " Download to High Fidelity"; buttons.children(":nth-child(2), .btn-group , .embed-button").each(function () { this.remove(); }); } + var downloadTimer; + function startAutoDownload() { + if (!downloadTimer) { + downloadTimer = setInterval(autoDownload, 1000); + } + } + function autoDownload() { + if ($("div.download-body").length !== 0) { + var downloadButton = $("div.download-body a.download-file"); + if (downloadButton.length > 0) { + clearInterval(downloadTimer); + downloadTimer = null; + var href = downloadButton[0].href; + EventBridge.emitWebEvent("CLARA.IO DOWNLOAD " + href); + console.log("Clara.io FBX file download initiated"); + $("a.btn.cancel").click(); + setTimeout(function () { window.open(href); }, 500); // Let cancel click take effect. + }; + } else { + clearInterval(downloadTimer); + downloadTimer = null; + } + } }'; - // Overload Clara FBX download link action. - property string replaceFBXDownload: 'var element = $("a.download-file"); - element.removeClass("download-file"); - element.removeAttr("download"); - element.bind("click", function(event) { - EventBridge.emitWebEvent("CLARA.IO DOWNLOAD"); - console.log("Clara.io FBX file download initiated for {uuid}"); - });' - property var notFbxHandler: 'var element = $("a.btn.btn-primary.viewer-button.download-file"); element.click();' @@ -139,7 +154,6 @@ Rectangle { // Catalog page. runJavaScript(updateLibraryPage, function() { console.log("Library link JS injection"); }); } - if (location.indexOf("clara.io/view/") !== -1) { // Item page. runJavaScript(updateItemPage, function() { console.log("Item link JS injection"); }); @@ -148,12 +162,6 @@ Rectangle { onLinkHovered: { desktop.currentUrl = hoveredUrl; - - if (File.isZippedFbx(desktop.currentUrl)) { - runJavaScript(replaceFBXDownload, function(){console.log("Download JS injection");}); - return; - } - if (File.isZipped(desktop.currentUrl)) { statusLabel.text = claraError; statusBar.color = hifi.colors.redHighlight; @@ -196,7 +204,8 @@ Rectangle { } function onWebEventReceived(event) { - if (event === "CLARA.IO DOWNLOAD") { + if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") { + desktop.currentUrl = event.slice(18); ApplicationInterface.addAssetToWorldInitiate(); } } From e7d9f98ca436b1488152da767563d936fb78f015 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 15 Nov 2016 09:23:51 +1300 Subject: [PATCH 030/101] Get script injection working in new marketplaces window --- interface/resources/qml/QmlWebWindow.qml | 13 +- libraries/ui/src/QmlWebWindowClass.cpp | 13 ++ libraries/ui/src/QmlWebWindowClass.h | 2 + scripts/system/html/js/marketplaces.js | 15 +- scripts/system/html/js/marketplacesClara.js | 13 ++ .../system/html/js/marketplacesDirectory.js | 13 ++ scripts/system/html/js/marketplacesHiFi.js | 13 ++ scripts/system/marketplaces/marketplaces.js | 142 ++++++++++++++++++ 8 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 scripts/system/html/js/marketplacesClara.js create mode 100644 scripts/system/html/js/marketplacesDirectory.js create mode 100644 scripts/system/html/js/marketplacesHiFi.js create mode 100644 scripts/system/marketplaces/marketplaces.js 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 From 1eeec2a3a149f5975761c26d76d01c800e03100e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 15 Nov 2016 10:24:38 +1300 Subject: [PATCH 031/101] Fix up script injection methods --- libraries/ui/src/QmlWebWindowClass.cpp | 6 +----- libraries/ui/src/QmlWebWindowClass.h | 3 +-- scripts/system/marketplaces/marketplaces.js | 8 ++++---- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index d799ff2db4..68bb872667 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -50,14 +50,10 @@ void QmlWebWindowClass::setURL(const QString& urlString) { }); } -void QmlWebWindowClass::setScriptUrl(const QString& script) { +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 3ed35217ca..15ebe74a4f 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -22,8 +22,7 @@ public: public slots: QString getURL() const; void setURL(const QString& url); - void setScriptUrl(const QString& script); - void clearScriptUrl (const QString& script); + void setScriptURL(const QString& script); signals: void urlChanged(); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index eae23cc65e..a817ff9673 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -15,7 +15,7 @@ Script.include("../libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("../assets/images/tools/"); -var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html");; +var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); var marketplaceWindow = new OverlayWebWindow({ title: "Marketplace", source: "about:blank", @@ -23,14 +23,14 @@ var marketplaceWindow = new OverlayWebWindow({ height: 700, visible: false }); -marketplaceWindow.setScriptUrl(Script.resolvePath("../html/js/marketplacesDirectory.js")); +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")); + marketplaceWindow.setScriptURL(Script.resolvePath("../html/js/marketplacesClara.js")); } if (data === "INJECT_HIFI") { - marketplaceWindow.setScriptUrl(Script.resolvePath("../html/js/marketplacesHiFi.js")); + marketplaceWindow.setScriptURL(Script.resolvePath("../html/js/marketplacesHiFi.js")); } }); From e4b8d340803ceccd21211916fce26da086f2f994 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 15 Nov 2016 14:38:03 +1300 Subject: [PATCH 032/101] Add navigation bar to marketplace pages --- scripts/system/html/css/edit-style.css | 14 +++++- scripts/system/html/css/marketplaces.css | 32 ++++++++++++- scripts/system/html/js/marketplacesClara.js | 46 ++++++++++++++++++- .../system/html/js/marketplacesDirectory.js | 2 +- scripts/system/html/js/marketplacesHiFi.js | 38 ++++++++++++++- scripts/system/html/marketplaces.html | 4 ++ scripts/system/marketplaces/marketplaces.js | 4 ++ 7 files changed, 135 insertions(+), 5 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index a5807ff025..91138e41ec 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -334,9 +334,14 @@ input[type=button].red { } input[type=button].blue { color: #fff; - background-color: #94132e; + background-color: #1080b8; background: linear-gradient(#00b4ef 20%, #1080b8 100%); } +input[type=button].white { + color: #121212; + background-color: #afafaf; + background: linear-gradient(#fff 20%, #afafaf 100%); +} input[type=button]:enabled:hover { background: linear-gradient(#000, #000); @@ -350,6 +355,10 @@ input[type=button].blue:enabled:hover { background: linear-gradient(#00b4ef, #00b4ef); border: none; } +input[type=button].white:enabled:hover { + background: linear-gradient(#fff, #fff); + border: none; +} input[type=button]:active { background: linear-gradient(#343434, #343434); @@ -360,6 +369,9 @@ input[type=button].red:active { input[type=button].blue:active { background: linear-gradient(#1080b8, #1080b8); } +input[type=button].white:active { + background: linear-gradient(#afafaf, #afafaf); +} input[type=button]:disabled { color: #252525; diff --git a/scripts/system/html/css/marketplaces.css b/scripts/system/html/css/marketplaces.css index 734501f3fc..0ec177ef62 100644 --- a/scripts/system/html/css/marketplaces.css +++ b/scripts/system/html/css/marketplaces.css @@ -22,11 +22,12 @@ body { margin-bottom: 20px; } .marketplaces-intro-text { - margin-bottom: 60px; + margin-bottom: 30px; } .marketplace-tile { float:left; width: 100%; + margin-bottom: 25px; } .marketplace-tile-first-column { text-align: center; @@ -75,6 +76,35 @@ body { .marketplace-clara-steps > li { margin-top: 5px; } + +#marketplace-navigation { + width: 100%; + height: 50px; + background: #00b4ef; + position: fixed; + bottom: 0; +} +#marketplace-navigation .glyph { + margin-left: 20px; + font-family: HiFi-Glyphs; + color: #fff; + font-size: 40px; + line-height: 50px; +} +#marketplace-navigation .text { + color: #fff; + font-size: 18px; + line-height: 50px; + vertical-align: top; + position: relative; + top: 1px; +} +#marketplace-navigation input { + float: right; + margin-right: 50px; + margin-top: 12px; +} + @media (max-width:768px) { .marketplace-tile-first-column { float: left; diff --git a/scripts/system/html/js/marketplacesClara.js b/scripts/system/html/js/marketplacesClara.js index 9eed23100e..f9b75899a8 100644 --- a/scripts/system/html/js/marketplacesClara.js +++ b/scripts/system/html/js/marketplacesClara.js @@ -10,4 +10,48 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -console.log("Hello from marketplacesClara.js"); +function onLoad() { + // Supporting styles from marketplaces.css. + $("head").append( + '' + ); + + // Supporting styles from edit-style.css. + $("head").append( + '' + ); + + // Make space for marketplaces footer. + $("head").append( + '' + ); + + // Marketplaces footer. + $("body").append( + '
' + + '[ Check out other marketplaces.' + + '' + + '
' + ); + + // Marketplace footer action. + $("#all-markets").on("click", function () { + $("#marketplace-content").attr("src", "marketplacesDirectory.html"); + EventBridge.emitWebEvent("RELOAD_DIRECTORY"); + }); +} + +window.addEventListener("load", onLoad); diff --git a/scripts/system/html/js/marketplacesDirectory.js b/scripts/system/html/js/marketplacesDirectory.js index b062735c99..dafd162c6e 100644 --- a/scripts/system/html/js/marketplacesDirectory.js +++ b/scripts/system/html/js/marketplacesDirectory.js @@ -10,4 +10,4 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -console.log("Hello from marketplacesDirectory.js"); +// Nothing to do; just need empty script to replace individual marketplace ones. diff --git a/scripts/system/html/js/marketplacesHiFi.js b/scripts/system/html/js/marketplacesHiFi.js index a74b9ce971..a84dc0779c 100644 --- a/scripts/system/html/js/marketplacesHiFi.js +++ b/scripts/system/html/js/marketplacesHiFi.js @@ -10,4 +10,40 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -console.log("Hello from marketplacesHiFi.js"); +function onLoad() { + // Supporting styles from marketplaces.css. + $("head").append( + '' + ); + + // Supporting styles from edit-style.css. + $("head").append( + '' + ); + + // Marketplaces footer. + $("body").append( + '
' + + '[ Check out other marketplaces.' + + '' + + '
' + ); + + // Marketplace footer action. + $("#all-markets").on("click", function () { + $("#marketplace-content").attr("src", "marketplacesDirectory.html"); + EventBridge.emitWebEvent("RELOAD_DIRECTORY"); + }); +} + +window.addEventListener("load", onLoad); diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 970af1158f..b2cfb359d0 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -49,5 +49,9 @@
+
+ [ Select a marketplace to explore. + +
\ No newline at end of file diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index a817ff9673..5a78e32e6b 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -32,6 +32,10 @@ marketplaceWindow.webEventReceived.connect(function (data) { if (data === "INJECT_HIFI") { marketplaceWindow.setScriptURL(Script.resolvePath("../html/js/marketplacesHiFi.js")); } + if (data === "RELOAD_DIRECTORY") { + marketplaceWindow.setScriptURL(Script.resolvePath("../html/js/marketplacesDirectory.js")); + marketplaceWindow.setURL(MARKETPLACES_URL); + } }); var toolHeight = 50; From 591db37a211788e42a927334ef2af657861832b4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 15 Nov 2016 15:09:33 +1300 Subject: [PATCH 033/101] Fix up navigation footer fonts --- scripts/system/html/css/marketplaces.css | 10 ++++++++-- scripts/system/html/js/marketplacesClara.js | 10 ++++++---- scripts/system/html/js/marketplacesHiFi.js | 10 ++++++---- scripts/system/html/marketplaces.html | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/scripts/system/html/css/marketplaces.css b/scripts/system/html/css/marketplaces.css index 0ec177ef62..0afec21919 100644 --- a/scripts/system/html/css/marketplaces.css +++ b/scripts/system/html/css/marketplaces.css @@ -85,10 +85,16 @@ body { bottom: 0; } #marketplace-navigation .glyph { - margin-left: 20px; + /* + // Target look but can't use font in injected script. font-family: HiFi-Glyphs; - color: #fff; font-size: 40px; + margin-left: 20px; + */ + font-family: sans-serif; + font-size: 24px; + margin-left: 30px; margin-right: 8px; + color: #fff; line-height: 50px; } #marketplace-navigation .text { diff --git a/scripts/system/html/js/marketplacesClara.js b/scripts/system/html/js/marketplacesClara.js index f9b75899a8..2185544769 100644 --- a/scripts/system/html/js/marketplacesClara.js +++ b/scripts/system/html/js/marketplacesClara.js @@ -12,19 +12,21 @@ function onLoad() { // Supporting styles from marketplaces.css. + // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. $("head").append( '' ); // Supporting styles from edit-style.css. + // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. $("head").append( '' ); // Supporting styles from edit-style.css. + // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. $("head").append( '' ); @@ -26,7 +26,7 @@ function onLoad() { // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. $("head").append( '' ); @@ -26,7 +26,7 @@ function onLoad() { // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. $("head").append( '' - ); +(function () { + // Can't use $(document).ready() because jQuery isn't loaded early enough by Clara Web page. - // Supporting styles from edit-style.css. - // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. - $("head").append( - '' - ); + var locationHref = ""; + var checkLocationInterval = undefined; - // Make space for marketplaces footer. - $("head").append( - '' - ); + function checkLocation() { + // Have to manually monitor location for changes because Clara Web page replaced content rather than loading new page. - // Marketplaces footer. - $("body").append( - '
' + - '🛈 Check out other markets.' + - '' + - '
' - ); + if (location.href !== locationHref) { - // Marketplace footer action. - $("#all-markets").on("click", function () { - $("#marketplace-content").attr("src", "marketplacesDirectory.html"); - EventBridge.emitWebEvent("RELOAD_DIRECTORY"); - }); -} + // Clara library page. + if (location.href.indexOf("clara.io/library") !== -1) { + var elements = $("a.thumbnail"); + for (var i = 0, length = elements.length; i < length; i++) { + var value = elements[i].getAttribute("href"); + if (value.slice(-6) !== "/image") { + elements[i].setAttribute("href", value + "/image"); + } + } + } -window.addEventListener("load", onLoad); + // Clara item page. + if (location.href.indexOf("clara.io/view/") !== -1) { + var element = $("a[href^=\'/library\']")[0]; + var parameters = "?gameCheck=true&public=true"; + var href = element.getAttribute("href"); + if (href.slice(-parameters.length) !== parameters) { + element.setAttribute("href", href + parameters); + } + var buttons = $("a.embed-button").parent("div"); + if (buttons.length > 0) { + var downloadFBX = buttons.find("a[data-extension=\'fbx\']")[0]; + downloadFBX.addEventListener("click", startAutoDownload); + var firstButton = buttons.children(":first-child")[0]; + buttons[0].insertBefore(downloadFBX, firstButton); + downloadFBX.setAttribute("class", "btn btn-primary download"); + downloadFBX.innerHTML = " Download to High Fidelity"; + buttons.children(":nth-child(2), .btn-group , .embed-button").each(function () { this.remove(); }); + } + var downloadTimer; + function startAutoDownload() { + if (!downloadTimer) { + downloadTimer = setInterval(autoDownload, 1000); + } + } + function autoDownload() { + if ($("div.download-body").length !== 0) { + var downloadButton = $("div.download-body a.download-file"); + if (downloadButton.length > 0) { + clearInterval(downloadTimer); + downloadTimer = null; + var href = downloadButton[0].href; + EventBridge.emitWebEvent("CLARA.IO DOWNLOAD " + href); + console.log("Clara.io FBX file download initiated"); + $("a.btn.cancel").click(); + setTimeout(function () { window.open(href); }, 500); // Let cancel click take effect. + }; + } else { + clearInterval(downloadTimer); + downloadTimer = null; + } + } + } + + locationHref = location.href; + } + } + + function onLoad() { + + // Supporting styles from marketplaces.css. + // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. + $("head").append( + '' + ); + + // Supporting styles from edit-style.css. + // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. + $("head").append( + '' + ); + + // Make space for marketplaces footer. + $("head").append( + '' + ); + + // Marketplaces footer. + $("body").append( + '
' + + '🛈 Check out other markets.' + + '' + + '
' + ); + + // Marketplace footer action. + $("#all-markets").on("click", function () { + $("#marketplace-content").attr("src", "marketplacesDirectory.html"); + EventBridge.emitWebEvent("RELOAD_DIRECTORY"); + }); + + checkLocation(); + checkLocationInterval = setInterval(checkLocation, 1000); + } + + function onUnload() { + clearInterval(checkLocationInterval); + checkLocationInterval = undefined; + locationHref = ""; + } + + window.addEventListener("load", onLoad); + window.addEventListener("unload", onUnload); + +}()); From d857f943f8314012f13ca893f085b100d461dcd6 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 7 Dec 2016 09:16:59 +1300 Subject: [PATCH 048/101] Handle Clara marketplace downloads --- interface/resources/qml/QmlWebWindow.qml | 10 ++++ interface/resources/qml/Web3DOverlay.qml | 27 ++++++++++ interface/src/Application.cpp | 54 +++++++++++++++++--- interface/src/Application.h | 10 +++- interface/src/ui/overlays/Web3DOverlay.cpp | 6 ++- libraries/networking/src/ResourceRequest.cpp | 13 +++++ libraries/networking/src/ResourceRequest.h | 1 + scripts/system/html/js/marketplacesClara.js | 3 +- 8 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 interface/resources/qml/Web3DOverlay.qml diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index c8c9b48699..d40b1595ba 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -99,6 +99,16 @@ Windows.ScrollingWindow { } userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] + + function onWebEventReceived(event) { + if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") { + ApplicationInterface.addAssetToWorldFromURL(event.slice(18)); + } + } + + Component.onCompleted: { + eventBridge.webEventReceived.connect(onWebEventReceived); + } } } } diff --git a/interface/resources/qml/Web3DOverlay.qml b/interface/resources/qml/Web3DOverlay.qml new file mode 100644 index 0000000000..e0689e614d --- /dev/null +++ b/interface/resources/qml/Web3DOverlay.qml @@ -0,0 +1,27 @@ +// +// Web3DOverlay.qml +// +// Created by David Rowe on 16 Dec 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +import "controls" as Controls + +Controls.WebView { + + function onWebEventReceived(event) { + if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") { + ApplicationInterface.addAssetToWorldFromURL(event.slice(18)); + } + } + + Component.onCompleted: { + eventBridge.webEventReceived.connect(onWebEventReceived); + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bbd9c081af..dd41e825f6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -42,6 +42,7 @@ #include #include +#include #include @@ -1803,9 +1804,9 @@ void Application::initializeUi() { rootContext->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); rootContext->setContextProperty("Controller", DependencyManager::get().data()); rootContext->setContextProperty("Entities", DependencyManager::get().data()); - FileScriptingInterface* fileDownload = new FileScriptingInterface(engine); - rootContext->setContextProperty("File", fileDownload); - connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::handleUnzip); + _fileDownload = new FileScriptingInterface(engine); + rootContext->setContextProperty("File", _fileDownload); + connect(_fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::handleUnzip); rootContext->setContextProperty("MyAvatar", getMyAvatar().get()); rootContext->setContextProperty("Messages", DependencyManager::get().data()); rootContext->setContextProperty("Recording", DependencyManager::get().data()); @@ -5457,15 +5458,52 @@ void Application::showAssetServerWidget(QString filePath) { startUpload(nullptr, nullptr); } -void Application::addAssetToWorldInitiate() { - qCDebug(interfaceapp) << "Start downloading asset file"; +void Application::addAssetToWorldFromURL(QString url) { + qInfo(interfaceapp) << "Download asset and add to world from" << url; + + QUrl urlURL = QUrl(url); + auto request = ResourceManager::createResourceRequest(nullptr, urlURL); + connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished); + request->send(); if (!_addAssetToWorldMessageBox) { - _addAssetToWorldMessageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, - "Downloading Asset", "Downloading asset file.", QMessageBox::Cancel, QMessageBox::NoButton); + _addAssetToWorldMessageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, + "Downloading Asset", "Downloading asset file " + url.section("filename=", 1, 1), + QMessageBox::Cancel, QMessageBox::NoButton); + } + connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed())); +} + +void Application::addAssetToWorldFromURLRequestFinished() { + auto request = qobject_cast(sender()); + auto url = request->getUrl().toString(); + auto result = request->getResult(); + + if (result == ResourceRequest::Success) { + qInfo(interfaceapp) << "Downloaded asset from" << url; + + QTemporaryDir temporaryDir; + temporaryDir.setAutoRemove(false); + QString temporaryDirPath = temporaryDir.path(); + QString filename = url.section("filename=", 1, 1); + QString downloadPath = temporaryDirPath + "/" + filename; + qInfo() << "Download path:" << downloadPath; + + QFile tempFile(downloadPath); + if (tempFile.open(QIODevice::WriteOnly)) { + tempFile.write(request->getData()); + qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true); + } else { + QString errorInfo = "Couldn't open temporary file for writing"; + qWarning(interfaceapp) << errorInfo; + addAssetToWorldError(errorInfo); + } + } else { + qWarning(interfaceapp) << "Error downloading" << url << ":" << request->getResultString(); + addAssetToWorldError("Error downloading " + url.section("filename=", 1, 1) + " : " + request->getResultString()); } - connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed())); + request->deleteLater(); } void Application::onAssetToWorldMessageBoxClosed() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 26fbe4ebb6..c83599747a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -310,7 +311,10 @@ public slots: void toggleLogDialog(); void toggleRunningScriptsWidget() const; Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); - Q_INVOKABLE void addAssetToWorldInitiate(); + + // FIXME: Move addAssetToWorld* methods to own class? + void addAssetToWorldFromURL(QString url); + void addAssetToWorldFromURLRequestFinished(); void addAssetToWorld(QString filePath); void addAssetToWorldWithNewMapping(QString path, QString mapping, int copy); void addAssetToWorldUpload(QString path, QString mapping); @@ -318,6 +322,8 @@ public slots: void addAssetToWorldAddEntity(QString mapping); void handleUnzip(QString filePath = "", bool autoAdd = false); + FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; } + void handleLocalServerConnection() const; void readArgumentsFromLocalSocket() const; @@ -627,6 +633,8 @@ private: QQuickItem* _addAssetToWorldMessageBox{ nullptr }; void addAssetToWorldError(QString errorText); + + FileScriptingInterface* _fileDownload; }; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 07adce75e5..fa04cd706a 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -112,11 +113,12 @@ void Web3DOverlay::render(RenderArgs* args) { // and the current rendering load) _webSurface->setMaxFps(10); _webSurface->create(currentContext); - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/")); - _webSurface->load("WebView.qml"); + _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); + _webSurface->load("Web3DOverlay.qml"); _webSurface->resume(); _webSurface->getRootItem()->setProperty("url", _url); _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); + _webSurface->getRootContext()->setContextProperty("ApplicationInterface", qApp); _webSurface->resize(QSize(_resolution.x, _resolution.y)); currentContext->makeCurrent(currentSurface); diff --git a/libraries/networking/src/ResourceRequest.cpp b/libraries/networking/src/ResourceRequest.cpp index bce6af7091..a374763fcf 100644 --- a/libraries/networking/src/ResourceRequest.cpp +++ b/libraries/networking/src/ResourceRequest.cpp @@ -26,3 +26,16 @@ void ResourceRequest::send() { _state = InProgress; doSend(); } + +QString ResourceRequest::getResultString() const { + switch (_result) { + case Success: return "Success"; + case Error: return "Error"; + case Timeout: return "Timeout"; + case ServerUnavailable: return "Server Unavailable"; + case AccessDenied: return "Access Denied"; + case InvalidURL: return "Ivalid URL"; + case NotFound: return "Not Found"; + default: return "Unspecified Error"; + } +} diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 83dc8fb7d7..46cdddd985 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -42,6 +42,7 @@ public: QByteArray getData() { return _data; } State getState() const { return _state; } Result getResult() const { return _result; } + QString getResultString() const; QUrl getUrl() const { return _url; } bool loadedFromCache() const { return _loadedFromCache; } diff --git a/scripts/system/html/js/marketplacesClara.js b/scripts/system/html/js/marketplacesClara.js index d4e5c870ed..77dbeec812 100644 --- a/scripts/system/html/js/marketplacesClara.js +++ b/scripts/system/html/js/marketplacesClara.js @@ -64,9 +64,8 @@ downloadTimer = null; var href = downloadButton[0].href; EventBridge.emitWebEvent("CLARA.IO DOWNLOAD " + href); - console.log("Clara.io FBX file download initiated"); + console.log("Clara.io FBX file download initiated for " + href); $("a.btn.cancel").click(); - setTimeout(function () { window.open(href); }, 500); // Let cancel click take effect. }; } else { clearInterval(downloadTimer); From 8e3067406957f2954b51bc0353a0ea92da149cfc Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 7 Dec 2016 09:28:35 +1300 Subject: [PATCH 049/101] Tidying --- interface/src/Application.cpp | 18 +++++++++--------- .../src/FileScriptingInterface.cpp | 2 -- scripts/system/html/js/marketplacesHiFi.js | 6 +++--- scripts/system/marketplaces/marketplaces.js | 14 +++++++------- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dd41e825f6..23460549b9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5526,7 +5526,7 @@ void Application::addAssetToWorld(QString filePath) { if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { QString errorInfo = "Do not have permissions to write to asset server."; - qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; addAssetToWorldError(errorInfo); return; } @@ -5551,7 +5551,7 @@ void Application::addAssetToWorldWithNewMapping(QString path, QString mapping, i } else if (result != GetMappingRequest::NoError) { QString errorInfo = "Could not map asset name: " + mapping.left(mapping.length() - QString::number(copy).length() - 1); - qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; addAssetToWorldError(errorInfo); } else if (copy < MAX_COPY_COUNT - 1) { if (copy > 0) { @@ -5563,7 +5563,7 @@ void Application::addAssetToWorldWithNewMapping(QString path, QString mapping, i } else { QString errorInfo = "Too many copies of asset name: " + mapping.left(mapping.length() - QString::number(copy).length() - 1); - qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; addAssetToWorldError(errorInfo); } request->deleteLater(); @@ -5581,7 +5581,7 @@ void Application::addAssetToWorldUpload(QString path, QString mapping) { QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable { if (upload->getError() != AssetUpload::NoError) { QString errorInfo = "Could not upload asset to asset server."; - qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; addAssetToWorldError(errorInfo); } else { addAssetToWorldSetMapping(mapping, hash); @@ -5610,7 +5610,7 @@ void Application::addAssetToWorldSetMapping(QString mapping, QString hash) { connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable { if (request->getError() != SetMappingRequest::NoError) { QString errorInfo = "Could not set asset mapping."; - qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; addAssetToWorldError(errorInfo); } else { addAssetToWorldAddEntity(mapping); @@ -5637,12 +5637,12 @@ void Application::addAssetToWorldAddEntity(QString mapping) { auto result = DependencyManager::get()->addEntity(properties); if (result == QUuid()) { - QString errorInfo = "Could not add downloaded asset " + mapping + " to world."; - qCDebug(interfaceapp) << "Error downloading asset: " + errorInfo; + QString errorInfo = "Could not add asset " + mapping + " to world."; + qWarning(interfaceapp) << "Could not add asset to world: " + errorInfo; addAssetToWorldError(errorInfo); } else { - QString successInfo = "Downloaded asset " + mapping + " added to world."; - qCDebug(interfaceapp) << "Downloading asset completed: " + successInfo; + QString successInfo = mapping + " added to world."; + qInfo() << "Downloading asset completed: " + successInfo; _addAssetToWorldMessageBox->setProperty("text", successInfo); _addAssetToWorldMessageBox->setProperty("buttons", QMessageBox::Ok); _addAssetToWorldMessageBox->setProperty("defaultButton", QMessageBox::Ok); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index cd9d92da8b..676158f606 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -80,7 +80,6 @@ bool FileScriptingInterface::isZipped(QUrl url) { return (url.toString().endsWith(".zip")); } -// this function is not in use QString FileScriptingInterface::getTempDir() { QTemporaryDir dir; dir.setAutoRemove(false); @@ -106,7 +105,6 @@ void FileScriptingInterface::downloadZip(QString path, const QString link) { request->send(); } - QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { QDir dir(path); diff --git a/scripts/system/html/js/marketplacesHiFi.js b/scripts/system/html/js/marketplacesHiFi.js index 562d20f584..3fa45ccd05 100644 --- a/scripts/system/html/js/marketplacesHiFi.js +++ b/scripts/system/html/js/marketplacesHiFi.js @@ -10,7 +10,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -function onLoad() { +$(document).ready(function () { + // Supporting styles from marketplaces.css. // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. $("head").append( @@ -46,6 +47,5 @@ function onLoad() { $("#marketplace-content").attr("src", "marketplacesDirectory.html"); EventBridge.emitWebEvent("RELOAD_DIRECTORY"); }); -} -window.addEventListener("load", onLoad); +}); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 2570bca308..49e64b91ab 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -29,12 +29,12 @@ var marketplaceWindow = new OverlayWebWindow({ }); marketplaceWindow.setScriptURL(MARKETPLACES_DIRECTORY_SCRIPT_URL); marketplaceWindow.webEventReceived.connect(function (data) { - if (data === "INJECT_CLARA") { - marketplaceWindow.setScriptURL(MARKETPLACES_CLARA_SCRIPT_URL); - } if (data === "INJECT_HIFI") { marketplaceWindow.setScriptURL(MARKETPLACES_HFIF_SCRIPT_URL); } + if (data === "INJECT_CLARA") { + marketplaceWindow.setScriptURL(MARKETPLACES_CLARA_SCRIPT_URL); + } if (data === "RELOAD_DIRECTORY") { marketplaceWindow.setScriptURL(MARKETPLACES_DIRECTORY_SCRIPT_URL); marketplaceWindow.setURL(MARKETPLACES_URL); @@ -47,7 +47,7 @@ var TOOLBAR_MARGIN_Y = 0; var marketplaceVisible = false; var marketplaceWebTablet; -// We persist clientOnly data in the .ini file, and reconsistitute it on restart. +// We persist clientOnly data in the .ini file, and reconstitute 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"; @@ -65,12 +65,12 @@ function showMarketplace(marketplaceID) { Settings.setValue(persistenceKey, marketplaceWebTablet.pickle()); marketplaceWebTablet.setScriptURL(MARKETPLACES_DIRECTORY_SCRIPT_URL); marketplaceWebTablet.getOverlayObject().webEventReceived.connect(function (data) { - if (data === "INJECT_CLARA") { - marketplaceWebTablet.setScriptURL(MARKETPLACES_CLARA_SCRIPT_URL); - } if (data === "INJECT_HIFI") { marketplaceWebTablet.setScriptURL(MARKETPLACES_HFIF_SCRIPT_URL); } + if (data === "INJECT_CLARA") { + marketplaceWebTablet.setScriptURL(MARKETPLACES_CLARA_SCRIPT_URL); + } if (data === "RELOAD_DIRECTORY") { marketplaceWebTablet.setScriptURL(MARKETPLACES_DIRECTORY_SCRIPT_URL); marketplaceWebTablet.setURL(""); // Force reload of URL. From 460d332c6f44a043f4e7865cbf6355eb39edd126 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 7 Dec 2016 10:06:32 +1300 Subject: [PATCH 050/101] Delete redundant code --- interface/resources/qml/Marketplaces.qml | 270 ------------------ .../src/FileScriptingInterface.cpp | 14 - .../src/FileScriptingInterface.h | 4 - scripts/system/marketplaces/clara.js | 85 ------ 4 files changed, 373 deletions(-) delete mode 100644 interface/resources/qml/Marketplaces.qml delete mode 100644 scripts/system/marketplaces/clara.js diff --git a/interface/resources/qml/Marketplaces.qml b/interface/resources/qml/Marketplaces.qml deleted file mode 100644 index ea5515b05f..0000000000 --- a/interface/resources/qml/Marketplaces.qml +++ /dev/null @@ -1,270 +0,0 @@ -// -// Marketplaces.qml -// -// Created by Elisa Lupin-Jimenez on 3 Aug 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 -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebChannel 1.0 -import QtWebEngine 1.2 -import QtWebSockets 1.0 - -import "controls" -import "controls-uit" as Controls -import "styles" -import "styles-uit" - - -Rectangle { - HifiConstants { id: hifi } - id: marketplace - anchors.fill: parent - - property var marketplacesUrl: "../../scripts/system/html/marketplaces.html" - property int statusBarHeight: 50 - property int statusMargin: 50 - property string standardMessage: "Check out other marketplaces." - property string claraMessage: "Choose a model and click Download -> Autodesk FBX." - property string claraError: "High Fidelity only supports Autodesk FBX models." - - onVisibleChanged: { - keyboardEnabled = HMD.active; - } - - Controls.BaseWebView { - id: webview - url: marketplacesUrl - anchors.top: marketplace.top - width: parent.width - height: parent.height - statusBarHeight - keyboard.height - focus: true - - webChannel.registeredObjects: [eventBridgeWrapper] - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] - - Timer { - id: alertTimer - running: false - repeat: false - interval: 9000 - property var handler; - onTriggered: handler(); - } - - function displayErrorStatus() { - alertTimer.handler = function() { - statusLabel.text = claraMessage; - statusBar.color = hifi.colors.blueHighlight; - statusIcon.text = hifi.glyphs.info; - } - alertTimer.start(); - } - - // In library page: - // - Open each item in "image" view. - property string updateLibraryPage: 'if ($) { - $(document).ready(function() { - var elements = $("a.thumbnail"); - for (var i = 0, length = elements.length; i < length; i++) { - var value = elements[i].getAttribute("href"); - if (value.slice(-6) !== "/image") { - elements[i].setAttribute("href", value + "/image"); - } - } - }); - }'; - - // In item page: - // - Fix up library link URL. - // - Reuse FBX download button as HiFi download button. - // - Remove "Edit Online", "Get Embed Code", and other download buttons. - property string updateItemPage: 'if ($) { - var element = $("a[href^=\'/library\']")[0]; - var parameters = "?gameCheck=true&public=true"; - var href = element.getAttribute("href"); - if (href.slice(-parameters.length) !== parameters) { - element.setAttribute("href", href + parameters); - } - var buttons = $("a.embed-button").parent("div"); - if (buttons.length > 0) { - var downloadFBX = buttons.find("a[data-extension=\'fbx\']")[0]; - downloadFBX.addEventListener("click", startAutoDownload); - var firstButton = buttons.children(":first-child")[0]; - buttons[0].insertBefore(downloadFBX, firstButton); - downloadFBX.setAttribute("class", "btn btn-primary download"); - downloadFBX.innerHTML = " Download to High Fidelity"; - buttons.children(":nth-child(2), .btn-group , .embed-button").each(function () { this.remove(); }); - } - var downloadTimer; - function startAutoDownload() { - if (!downloadTimer) { - downloadTimer = setInterval(autoDownload, 1000); - } - } - function autoDownload() { - if ($("div.download-body").length !== 0) { - var downloadButton = $("div.download-body a.download-file"); - if (downloadButton.length > 0) { - clearInterval(downloadTimer); - downloadTimer = null; - var href = downloadButton[0].href; - EventBridge.emitWebEvent("CLARA.IO DOWNLOAD " + href); - console.log("Clara.io FBX file download initiated"); - $("a.btn.cancel").click(); - setTimeout(function () { window.open(href); }, 500); // Let cancel click take effect. - }; - } else { - clearInterval(downloadTimer); - downloadTimer = null; - } - } - }'; - - property var notFbxHandler: 'var element = $("a.btn.btn-primary.viewer-button.download-file"); - element.click();' - - property var autoCancel: 'var element = $("a.btn.cancel"); - element.click();' - - onUrlChanged: { - var location = url.toString(); - if (location.indexOf("clara.io/library") !== -1) { - // Catalog page. - runJavaScript(updateLibraryPage, function() { console.log("Library link JS injection"); }); - } - if (location.indexOf("clara.io/view/") !== -1) { - // Item page. - runJavaScript(updateItemPage, function() { console.log("Item link JS injection"); }); - } - } - - onLinkHovered: { - desktop.currentUrl = hoveredUrl; - if (File.isZipped(desktop.currentUrl)) { - statusLabel.text = claraError; - statusBar.color = hifi.colors.redHighlight; - statusIcon.text = hifi.glyphs.alert; - runJavaScript(notFbxHandler, displayErrorStatus()); - } - } - - onLoadingChanged: { - if (File.isClaraLink(webview.url)) { - statusLabel.text = claraMessage; - } else { - statusLabel.text = standardMessage; - } - statusBar.color = hifi.colors.blueHighlight; - statusIcon.text = hifi.glyphs.info; - } - - onNewViewRequested: { - var component = Qt.createComponent("Browser.qml"); - var newWindow = component.createObject(desktop); - - // Hide brief flash of browser window behind marketplace window. - newWindow.x = x; - newWindow.y = y; - newWindow.z = 0; - newWindow.width = 0; - newWindow.height = 0; - - request.openIn(newWindow.webView); - if (File.isZippedFbx(desktop.currentUrl)) { - newWindow.setAutoAdd(true); - runJavaScript(autoCancel); - newWindow.loadingChanged.connect(function(status) { - if (status > 0) { - newWindow.destroy(); // Download has kicked off so we can destroy the Web window. - } - }); - } - } - - function onWebEventReceived(event) { - if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") { - desktop.currentUrl = event.slice(18); - ApplicationInterface.addAssetToWorldInitiate(); - } - } - - Component.onCompleted: { - eventBridge.webEventReceived.connect(onWebEventReceived); - } - } - - Rectangle { - id: statusBar - anchors.top: webview.bottom - anchors.bottom: keyboard.top - anchors.left: parent.left - anchors.right: parent.right - color: hifi.colors.blueHighlight - - Controls.Button { - id: switchMarketView - anchors.right: parent.right - anchors.rightMargin: statusMargin - anchors.verticalCenter: parent.verticalCenter - width: 150 - text: "See all markets" - onClicked: { - webview.url = marketplacesUrl; - statusLabel.text = standardMessage; - } - } - - Controls.Label { - id: statusLabel - anchors.verticalCenter: switchMarketView.verticalCenter - anchors.left: parent.left - anchors.leftMargin: statusMargin - color: hifi.colors.white - text: standardMessage - size: 18 - } - - HiFiGlyphs { - id: statusIcon - anchors.right: statusLabel.left - anchors.verticalCenter: statusLabel.verticalCenter - text: hifi.glyphs.info - color: hifi.colors.white - size: hifi.fontSizes.tableHeadingIcon - } - } - - Controls.Keyboard { - id: keyboard - enabled: keyboardEnabled - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } -} diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 676158f606..3c2222da6f 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -66,20 +66,6 @@ bool FileScriptingInterface::isTempDir(QString tempDir) { return (testContainer == tempContainer); } -// checks whether the webview is displaying a Clara.io page for Marketplaces.qml -bool FileScriptingInterface::isClaraLink(QUrl url) { - return (url.toString().contains("clara.io") && !url.toString().contains("clara.io/signup")); -} - -bool FileScriptingInterface::isZippedFbx(QUrl url) { - return (url.toString().endsWith("fbx.zip")); -} - -// checks whether a user tries to download a file that is not in .fbx format -bool FileScriptingInterface::isZipped(QUrl url) { - return (url.toString().endsWith(".zip")); -} - QString FileScriptingInterface::getTempDir() { QTemporaryDir dir; dir.setAutoRemove(false); diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 900aec2b0b..6a793d79f8 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -22,11 +22,7 @@ class FileScriptingInterface : public QObject { public: FileScriptingInterface(QObject* parent); - public slots: - bool isZippedFbx(QUrl url); - bool isZipped(QUrl url); - bool isClaraLink(QUrl url); QString convertUrlToPath(QUrl url); void runUnzip(QString path, QUrl url, bool autoAdd); QString getTempDir(); diff --git a/scripts/system/marketplaces/clara.js b/scripts/system/marketplaces/clara.js deleted file mode 100644 index ab1e5131bc..0000000000 --- a/scripts/system/marketplaces/clara.js +++ /dev/null @@ -1,85 +0,0 @@ -"use strict"; - -// -// clara.js -// -// Created by Eric Levin on 8 Jan 2016 -// Edited by Elisa Lupin-Jimenez on 23 Aug 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 - -var toolIconUrl = Script.resolvePath("../assets/images/tools/"); -var qml = Script.resolvePath("../../../resources/qml/Marketplaces.qml") - -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; - -var marketplaceWindow = new OverlayWindow({ - title: "Marketplaces", - source: qml, - width: 1000, - height: 900, - toolWindow: false, - visible: false, -}); - -var toolHeight = 50; -var toolWidth = 50; -var TOOLBAR_MARGIN_Y = 0; - - -function showMarketplace(marketplaceID) { - var url = MARKETPLACE_URL; - if (marketplaceID) { - url = url + "/items/" + marketplaceID; - } - marketplaceWindow.setVisible(true); - - UserActivityLogger.openedMarketplace(); -} - -function hideMarketplace() { - marketplaceWindow.setVisible(false); -} - -function toggleMarketplace() { - if (marketplaceWindow.visible) { - 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 onExamplesWindowVisibilityChanged() { - browseExamplesButton.writeProperty('buttonState', marketplaceWindow.visible ? 0 : 1); - browseExamplesButton.writeProperty('defaultState', marketplaceWindow.visible ? 0 : 1); - browseExamplesButton.writeProperty('hoverState', marketplaceWindow.visible ? 2 : 3); -} -function onClick() { - toggleMarketplace(); -} -browseExamplesButton.clicked.connect(onClick); -marketplaceWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); - -Script.scriptEnding.connect(function () { - toolBar.removeButton("marketplace"); - browseExamplesButton.clicked.disconnect(onClick); - marketplaceWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); -}); - -}()); // END LOCAL_SCOPE From 641e47e0c51f9f292c8493faf3b69f72adaa0589 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 7 Dec 2016 10:39:37 +1300 Subject: [PATCH 051/101] Fix build warnings --- interface/src/Application.h | 2 +- interface/src/ui/overlays/Overlays.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index c83599747a..85b8873282 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -588,7 +588,7 @@ private: DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); ThreadSafeValueCache _keyboardFocusedEntity; - ThreadSafeValueCache _keyboardFocusedOverlay; + ThreadSafeValueCache _keyboardFocusedOverlay; quint64 _lastAcceptedKeyPress = 0; bool _isForeground = true; // starts out assumed to be in foreground bool _inPaint = false; diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 1fed3bc1fb..5ca4f36a30 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -53,7 +53,7 @@ class RayToOverlayIntersectionResult { public: RayToOverlayIntersectionResult(); bool intersects; - int overlayID; + unsigned int overlayID; float distance; BoxFace face; glm::vec3 surfaceNormal; From 186d3f18ef0806df21571742b0aaafab654f1bfc Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 7 Dec 2016 11:55:17 +1300 Subject: [PATCH 052/101] Improve temporary download directory robustness --- interface/src/Application.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fb9390ca65..f10bbe5de3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5488,20 +5488,25 @@ void Application::addAssetToWorldFromURLRequestFinished() { if (result == ResourceRequest::Success) { qInfo(interfaceapp) << "Downloaded asset from" << url; - QTemporaryDir temporaryDir; temporaryDir.setAutoRemove(false); - QString temporaryDirPath = temporaryDir.path(); - QString filename = url.section("filename=", 1, 1); - QString downloadPath = temporaryDirPath + "/" + filename; - qInfo() << "Download path:" << downloadPath; + if (temporaryDir.isValid()) { + QString temporaryDirPath = temporaryDir.path(); + QString filename = url.section("filename=", 1, 1); + QString downloadPath = temporaryDirPath + "/" + filename; + qInfo() << "Download path:" << downloadPath; - QFile tempFile(downloadPath); - if (tempFile.open(QIODevice::WriteOnly)) { - tempFile.write(request->getData()); - qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true); + QFile tempFile(downloadPath); + if (tempFile.open(QIODevice::WriteOnly)) { + tempFile.write(request->getData()); + qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true); + } else { + QString errorInfo = "Couldn't open temporary file for download"; + qWarning(interfaceapp) << errorInfo; + addAssetToWorldError(errorInfo); + } } else { - QString errorInfo = "Couldn't open temporary file for writing"; + QString errorInfo = "Couldn't create temporary directory for download"; qWarning(interfaceapp) << errorInfo; addAssetToWorldError(errorInfo); } From 01226b03ef4f2561e6ca00b6dc3f4cc3846f3dba Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 7 Dec 2016 12:20:43 +1300 Subject: [PATCH 053/101] Improve text and robustness of marketplaces footer --- scripts/system/html/css/marketplaces.css | 10 ++++++---- scripts/system/html/js/marketplacesClara.js | 6 +++--- scripts/system/html/js/marketplacesHiFi.js | 6 +++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/scripts/system/html/css/marketplaces.css b/scripts/system/html/css/marketplaces.css index 2893f089bd..7660584730 100644 --- a/scripts/system/html/css/marketplaces.css +++ b/scripts/system/html/css/marketplaces.css @@ -93,8 +93,8 @@ body { */ font-family: sans-serif; font-size: 24px; - margin-left: 25px; - margin-right: 5px; + margin-left: 20px; + margin-right: 3px; color: #fff; line-height: 50px; } @@ -107,9 +107,11 @@ body { top: 1px; } #marketplace-navigation input { - float: right; - margin-right: 25px; + position: absolute; + right: 20px; margin-top: 12px; + padding-left: 10px; + padding-right: 10px; } @media (max-width:768px) { diff --git a/scripts/system/html/js/marketplacesClara.js b/scripts/system/html/js/marketplacesClara.js index 77dbeec812..8540b36a70 100644 --- a/scripts/system/html/js/marketplacesClara.js +++ b/scripts/system/html/js/marketplacesClara.js @@ -85,9 +85,9 @@ $("head").append( '' ); @@ -113,7 +113,7 @@ // Marketplaces footer. $("body").append( '
' + - '🛈 Check out other markets.' + + '🛈 Check out other marketplaces.' + '' + '
' ); diff --git a/scripts/system/html/js/marketplacesHiFi.js b/scripts/system/html/js/marketplacesHiFi.js index 3fa45ccd05..3aba2937c4 100644 --- a/scripts/system/html/js/marketplacesHiFi.js +++ b/scripts/system/html/js/marketplacesHiFi.js @@ -17,9 +17,9 @@ $(document).ready(function () { $("head").append( '' ); @@ -37,7 +37,7 @@ $(document).ready(function () { // Marketplaces footer. $("body").append( '
' + - '🛈 Check out other markets.' + + '🛈 Check out other marketplaces.' + '' + '
' ); From e821ec55932abacafc711a3d07136b450a5e407f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 7 Dec 2016 12:38:16 +1300 Subject: [PATCH 054/101] Inject navigation footer into Clara account createion page --- scripts/system/html/js/marketplaces.js | 9 ++++++--- scripts/system/html/marketplaces.html | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/js/marketplaces.js b/scripts/system/html/js/marketplaces.js index 7b529184e3..d4637a4519 100644 --- a/scripts/system/html/js/marketplaces.js +++ b/scripts/system/html/js/marketplaces.js @@ -12,12 +12,15 @@ function loaded() { } function bindExploreButtons() { + $('#claraSignUp').on('click', function () { + EventBridge.emitWebEvent("INJECT_CLARA"); + }); $('#exploreClaraMarketplace').on('click', function () { EventBridge.emitWebEvent("INJECT_CLARA"); window.location = "https://clara.io/library?gameCheck=true&public=true" - }) + }); $('#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/marketplaces.html b/scripts/system/html/marketplaces.html index 8b60cae838..c9ee2af8a3 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -39,7 +39,7 @@

Clara.io has thousands of models available for importing into High Fidelity. Follow these steps for the best experience:

    -
  1. Create an account here or log in as an existing user.
  2. +
  3. Create an account here or log in as an existing user.
  4. Choose a model from the list and click “Download to High Fidelity”.
From 1de2c14fee7ce510c5a6348867693078dce0eeac Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 7 Dec 2016 12:52:53 +1300 Subject: [PATCH 055/101] Remove e-mail message hyperlink if displaying on tablet --- scripts/system/html/js/marketplacesDirectory.js | 10 +++++++++- scripts/system/html/marketplaces.html | 2 +- scripts/system/marketplaces/marketplaces.js | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/js/marketplacesDirectory.js b/scripts/system/html/js/marketplacesDirectory.js index dafd162c6e..cffb5c061d 100644 --- a/scripts/system/html/js/marketplacesDirectory.js +++ b/scripts/system/html/js/marketplacesDirectory.js @@ -10,4 +10,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// Nothing to do; just need empty script to replace individual marketplace ones. +// Only used in WebTablet. + +$(document).ready(function () { + + // Remove e-mail hyperlink. + var letUsKnow = $("#letUsKnow"); + letUsKnow.replaceWith(letUsKnow.html()); + +}); diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index c9ee2af8a3..a90532efad 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -19,7 +19,7 @@

Marketplaces

-

You can bring content into High Fidelity from anywhere you want. Here are a few places that support direct import of content right now. If you'd like to suggest a Market to include here, let us know.

+

You can bring content into High Fidelity from anywhere you want. Here are a few places that support direct import of content right now. If you'd like to suggest a Market to include here, let us know.

diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 49e64b91ab..89b28dfa2f 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -27,7 +27,7 @@ var marketplaceWindow = new OverlayWebWindow({ height: 700, visible: false }); -marketplaceWindow.setScriptURL(MARKETPLACES_DIRECTORY_SCRIPT_URL); +marketplaceWindow.setScriptURL(""); marketplaceWindow.webEventReceived.connect(function (data) { if (data === "INJECT_HIFI") { marketplaceWindow.setScriptURL(MARKETPLACES_HFIF_SCRIPT_URL); @@ -36,7 +36,7 @@ marketplaceWindow.webEventReceived.connect(function (data) { marketplaceWindow.setScriptURL(MARKETPLACES_CLARA_SCRIPT_URL); } if (data === "RELOAD_DIRECTORY") { - marketplaceWindow.setScriptURL(MARKETPLACES_DIRECTORY_SCRIPT_URL); + marketplaceWindow.setScriptURL(""); marketplaceWindow.setURL(MARKETPLACES_URL); } }); From c4c2a95e837ca226693e03dd4b3c88f4c2b04914 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 8 Dec 2016 10:58:37 +1300 Subject: [PATCH 056/101] Add "back" button to marketplaces footer --- scripts/system/html/css/marketplaces.css | 16 +- scripts/system/html/js/marketplaces.js | 26 --- .../system/html/js/marketplacesDirectory.js | 21 --- scripts/system/html/js/marketplacesHiFi.js | 51 ------ ...etplacesClara.js => marketplacesInject.js} | 161 ++++++++++++------ scripts/system/html/marketplaces.html | 7 +- scripts/system/marketplaces/marketplaces.js | 33 +--- 7 files changed, 130 insertions(+), 185 deletions(-) delete mode 100644 scripts/system/html/js/marketplaces.js delete mode 100644 scripts/system/html/js/marketplacesDirectory.js delete mode 100644 scripts/system/html/js/marketplacesHiFi.js rename scripts/system/html/js/{marketplacesClara.js => marketplacesInject.js} (61%) diff --git a/scripts/system/html/css/marketplaces.css b/scripts/system/html/css/marketplaces.css index 7660584730..bb57bea3bc 100644 --- a/scripts/system/html/css/marketplaces.css +++ b/scripts/system/html/css/marketplaces.css @@ -110,8 +110,8 @@ body { position: absolute; right: 20px; margin-top: 12px; - padding-left: 10px; - padding-right: 10px; + padding-left: 15px; + padding-right: 15px; } @media (max-width:768px) { @@ -128,10 +128,10 @@ body { text-align:center; } .tile-divider { - width: 100%; - margin-left: 0%; + width: 100%; + margin-left: 0; + } + .marketplace-tile-image { + margin-bottom: 15px; + } } -.marketplace-tile-image{ - margin-bottom:15px; -} -} \ No newline at end of file diff --git a/scripts/system/html/js/marketplaces.js b/scripts/system/html/js/marketplaces.js deleted file mode 100644 index d4637a4519..0000000000 --- a/scripts/system/html/js/marketplaces.js +++ /dev/null @@ -1,26 +0,0 @@ -// -// 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() { - $('#claraSignUp').on('click', function () { - EventBridge.emitWebEvent("INJECT_CLARA"); - }); - $('#exploreClaraMarketplace').on('click', function () { - EventBridge.emitWebEvent("INJECT_CLARA"); - window.location = "https://clara.io/library?gameCheck=true&public=true" - }); - $('#exploreHifiMarketplace').on('click', function () { - EventBridge.emitWebEvent("INJECT_HIFI"); - window.location = "http://www.highfidelity.com/marketplace" - }); -} diff --git a/scripts/system/html/js/marketplacesDirectory.js b/scripts/system/html/js/marketplacesDirectory.js deleted file mode 100644 index cffb5c061d..0000000000 --- a/scripts/system/html/js/marketplacesDirectory.js +++ /dev/null @@ -1,21 +0,0 @@ -// -// 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 -// - -// Only used in WebTablet. - -$(document).ready(function () { - - // Remove e-mail hyperlink. - var letUsKnow = $("#letUsKnow"); - letUsKnow.replaceWith(letUsKnow.html()); - -}); diff --git a/scripts/system/html/js/marketplacesHiFi.js b/scripts/system/html/js/marketplacesHiFi.js deleted file mode 100644 index 3aba2937c4..0000000000 --- a/scripts/system/html/js/marketplacesHiFi.js +++ /dev/null @@ -1,51 +0,0 @@ -// -// 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 -// - -$(document).ready(function () { - - // Supporting styles from marketplaces.css. - // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. - $("head").append( - '' - ); - - // Supporting styles from edit-style.css. - // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. - $("head").append( - '' - ); - - // Marketplaces footer. - $("body").append( - '
' + - '🛈 Check out other marketplaces.' + - '' + - '
' - ); - - // Marketplace footer action. - $("#all-markets").on("click", function () { - $("#marketplace-content").attr("src", "marketplacesDirectory.html"); - EventBridge.emitWebEvent("RELOAD_DIRECTORY"); - }); - -}); diff --git a/scripts/system/html/js/marketplacesClara.js b/scripts/system/html/js/marketplacesInject.js similarity index 61% rename from scripts/system/html/js/marketplacesClara.js rename to scripts/system/html/js/marketplacesInject.js index 8540b36a70..401185f290 100644 --- a/scripts/system/html/js/marketplacesClara.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -1,25 +1,84 @@ // -// marketplacesClara.js +// marketplacesInject.js // // Created by David Rowe on 12 Nov 2016. // Copyright 2016 High Fidelity, Inc. // -// Injected into Clara.io marketplace Web page. +// Injected into marketplace Web pages. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // (function () { - // Can't use $(document).ready() because jQuery isn't loaded early enough by Clara Web page. - var locationHref = ""; - var checkLocationInterval = undefined; + function injectCommonCode(isDirectoryPage) { - function checkLocation() { + // Supporting styles from marketplaces.css. + // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. + $("head").append( + '' + ); + + // Supporting styles from edit-style.css. + // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. + $("head").append( + '' + ); + + // Footer. + var isInitialDirectoryPage = location.href.match(/\/scripts\/system\/html\/marketplaces\.html$/); + $("body").append( + '
' + + (isInitialDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + + (!isInitialDirectoryPage ? '' : '') + + (!isDirectoryPage ? '' : '') + + '
' + ); + + // Footer actions. + $("#back-button").on("click", function () { + window.history.back(); + }); + $("#all-markets").on("click", function () { + EventBridge.emitWebEvent("GOTO_DIRECTORY"); + }); + } + + function injectDirectoryCode() { + + // Remove e-mail hyperlink. + var letUsKnow = $("#letUsKnow"); + letUsKnow.replaceWith(letUsKnow.html()); + + // Add button links. + $('#exploreClaraMarketplace').on('click', function () { + window.location = "https://clara.io/library?gameCheck=true&public=true" + }); + $('#exploreHifiMarketplace').on('click', function () { + window.location = "http://www.highfidelity.com/marketplace" + }); + } + + function injectHiFiCode() { + // Nothing to do. + } + + function updateClaraCode(currentLocation) { // Have to manually monitor location for changes because Clara Web page replaced content rather than loading new page. - if (location.href !== locationHref) { + if (location.href !== currentLocation) { // Clara library page. if (location.href.indexOf("clara.io/library") !== -1) { @@ -74,35 +133,13 @@ } } - locationHref = location.href; + currentLocation = location.href; } } - function onLoad() { + function injectClaraCode() { - // Supporting styles from marketplaces.css. - // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. - $("head").append( - '' - ); - - // Supporting styles from edit-style.css. - // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. - $("head").append( - '' - ); - - // Make space for marketplaces footer. + // Make space for marketplaces footer in Clara pages. $("head").append( '' ); - // Marketplaces footer. - $("body").append( - '
' + - '🛈 Check out other marketplaces.' + - '' + - '
' - ); + // Update code injected per page displayed. + var currentLocation = ""; + var checkLocationInterval = undefined; + updateClaraCode(currentLocation); + checkLocationInterval = setInterval(function () { + updateClaraCode(currentLocation); + }, 1000); - // Marketplace footer action. - $("#all-markets").on("click", function () { - $("#marketplace-content").attr("src", "marketplacesDirectory.html"); - EventBridge.emitWebEvent("RELOAD_DIRECTORY"); + window.addEventListener("unload", function () { + clearInterval(checkLocationInterval); + checkLocationInterval = undefined; + currentLocation = ""; }); - - checkLocation(); - checkLocationInterval = setInterval(checkLocation, 1000); } - function onUnload() { - clearInterval(checkLocationInterval); - checkLocationInterval = undefined; - locationHref = ""; + function onLoad() { + var DIRECTORY = 0; + var HIFI = 1; + var CLARA = 2; + var pageType = DIRECTORY; + + if (location.href.indexOf("highfidelity.com/") !== -1) { pageType = HIFI; } + if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } + + injectCommonCode(pageType === DIRECTORY); + switch (pageType) { + case DIRECTORY: + injectDirectoryCode(); + break; + case HIFI: + injectHiFiCode(); + break; + case CLARA: + injectClaraCode(); + break; + } } - window.addEventListener("load", onLoad); - window.addEventListener("unload", onUnload); + // Load / unload. + try { + // This appears more responsive to the user but $ is not necessarily loaded in time for each marketplace. + $(document).ready(function () { onLoad(); }); + } + catch (e) { + window.addEventListener("load", onLoad); + } }()); diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index a90532efad..e2e15d83cc 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -13,9 +13,8 @@ - - +

Marketplaces

@@ -49,9 +48,11 @@
+ +
+ --> \ No newline at end of file diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 89b28dfa2f..3953ed1508 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -16,9 +16,8 @@ Script.include("../libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("../assets/images/tools/"); var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); -var MARKETPLACES_DIRECTORY_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesDirectory.js"); -var MARKETPLACES_HFIF_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesHiFi.js"); -var MARKETPLACES_CLARA_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesClara.js"); +var MARKETPLACES_URL_AMENDED = MARKETPLACES_URL + "?"; // Append "?" to signal injected script that it's not the initial page. +var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); var marketplaceWindow = new OverlayWebWindow({ title: "Marketplace", @@ -27,17 +26,10 @@ var marketplaceWindow = new OverlayWebWindow({ height: 700, visible: false }); -marketplaceWindow.setScriptURL(""); +marketplaceWindow.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); marketplaceWindow.webEventReceived.connect(function (data) { - if (data === "INJECT_HIFI") { - marketplaceWindow.setScriptURL(MARKETPLACES_HFIF_SCRIPT_URL); - } - if (data === "INJECT_CLARA") { - marketplaceWindow.setScriptURL(MARKETPLACES_CLARA_SCRIPT_URL); - } - if (data === "RELOAD_DIRECTORY") { - marketplaceWindow.setScriptURL(""); - marketplaceWindow.setURL(MARKETPLACES_URL); + if (data === "GOTO_DIRECTORY") { + marketplaceWindow.setURL(MARKETPLACES_URL_AMENDED); } }); @@ -63,18 +55,11 @@ function showMarketplace(marketplaceID) { updateButtonState(true); marketplaceWebTablet = new WebTablet(MARKETPLACES_URL, null, null, true); Settings.setValue(persistenceKey, marketplaceWebTablet.pickle()); - marketplaceWebTablet.setScriptURL(MARKETPLACES_DIRECTORY_SCRIPT_URL); + marketplaceWebTablet.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); marketplaceWebTablet.getOverlayObject().webEventReceived.connect(function (data) { - if (data === "INJECT_HIFI") { - marketplaceWebTablet.setScriptURL(MARKETPLACES_HFIF_SCRIPT_URL); - } - if (data === "INJECT_CLARA") { - marketplaceWebTablet.setScriptURL(MARKETPLACES_CLARA_SCRIPT_URL); - } - if (data === "RELOAD_DIRECTORY") { - marketplaceWebTablet.setScriptURL(MARKETPLACES_DIRECTORY_SCRIPT_URL); - marketplaceWebTablet.setURL(""); // Force reload of URL. - marketplaceWebTablet.setURL(MARKETPLACES_URL); + if (data === "GOTO_DIRECTORY") { + marketplaceWebTablet.setURL(""); // Force reload of URL to work around WetTablet bug. + marketplaceWebTablet.setURL(MARKETPLACES_URL_AMENDED); } }); } else { From 639d7a5d97895ce8a8832e3e2fe938476776bd18 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 8 Dec 2016 18:36:50 +1300 Subject: [PATCH 057/101] Add further user feedback on Clara download progress --- interface/src/Application.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f10bbe5de3..05dbc0ca13 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5546,6 +5546,8 @@ void Application::addAssetToWorld(QString filePath) { QString path = QUrl(filePath).toLocalFile(); QString mapping = path.right(path.length() - path.lastIndexOf("/")); + _addAssetToWorldMessageBox->setProperty("text", "Adding " + mapping.mid(1) + " to Asset Server"); + addAssetToWorldWithNewMapping(path, mapping, 0); } @@ -5653,7 +5655,7 @@ void Application::addAssetToWorldAddEntity(QString mapping) { qWarning(interfaceapp) << "Could not add asset to world: " + errorInfo; addAssetToWorldError(errorInfo); } else { - QString successInfo = mapping + " added to world."; + QString successInfo = "Asset " + mapping.mid(1) + " added to world."; qInfo() << "Downloading asset completed: " + successInfo; _addAssetToWorldMessageBox->setProperty("text", successInfo); _addAssetToWorldMessageBox->setProperty("buttons", QMessageBox::Ok); From 7eacdb69b553302730528f90a97fbefca7433feb Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 8 Dec 2016 19:17:26 +1300 Subject: [PATCH 058/101] Auto-resize Clara.io models if larger than 10m as likely in cm or mm --- interface/src/Application.cpp | 77 +++++++++++++++++++++++++++++++++-- interface/src/Application.h | 4 ++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 05dbc0ca13..35e1e87303 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1428,6 +1428,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _connectionMonitor.init(); + // Monitor model assets (e.g., from Clara.io) added to the world that may need resizing. + static const int ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS = 1000; + _addAssetToWorldTimer.setInterval(ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS); + connect(&_addAssetToWorldTimer, &QTimer::timeout, this, &Application::addAssetToWorldCheckModelSize); + // After all of the constructor is completed, then set firstRun to false. firstRun.set(false); } @@ -5546,7 +5551,7 @@ void Application::addAssetToWorld(QString filePath) { QString path = QUrl(filePath).toLocalFile(); QString mapping = path.right(path.length() - path.lastIndexOf("/")); - _addAssetToWorldMessageBox->setProperty("text", "Adding " + mapping.mid(1) + " to Asset Server"); + _addAssetToWorldMessageBox->setProperty("text", "Adding " + mapping.mid(1) + " to Asset Server."); addAssetToWorldWithNewMapping(path, mapping, 0); } @@ -5645,16 +5650,26 @@ void Application::addAssetToWorldAddEntity(QString mapping) { properties.setName(mapping.right(mapping.length() - 1)); properties.setModelURL("atp:" + mapping); properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); + properties.setCollisionless(true); // Temporarily set so that doesn't collide with avatar. properties.setDynamic(false); properties.setPosition(getMyAvatar()->getPosition() + getMyAvatar()->getOrientation() * glm::vec3(0.0f, 0.0f, -2.0f)); properties.setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); - auto result = DependencyManager::get()->addEntity(properties); + auto entityID = DependencyManager::get()->addEntity(properties); + // Note: Model dimensions are not available here; model is scaled per FBX mesh in RenderableModelEntityItem::update() later + // on. But FBX dimensions may be in cm or mm, so we monitor for the dimension change and rescale again if warranted. - if (result == QUuid()) { + if (entityID == QUuid()) { QString errorInfo = "Could not add asset " + mapping + " to world."; qWarning(interfaceapp) << "Could not add asset to world: " + errorInfo; addAssetToWorldError(errorInfo); } else { + // Monitor when asset is rendered in world so that can resize if necessary. + _addAssetToWorldResizeList.insert(entityID, 0); // List value is count of checks performed. + if (!_addAssetToWorldTimer.isActive()) { + _addAssetToWorldTimer.start(); + } + + // Inform user. QString successInfo = "Asset " + mapping.mid(1) + " added to world."; qInfo() << "Downloading asset completed: " + successInfo; _addAssetToWorldMessageBox->setProperty("text", successInfo); @@ -5663,6 +5678,62 @@ void Application::addAssetToWorldAddEntity(QString mapping) { } } +void Application::addAssetToWorldCheckModelSize() { + if (_addAssetToWorldResizeList.size() == 0) { + return; + } + + auto item = _addAssetToWorldResizeList.begin(); + while (item != _addAssetToWorldResizeList.end()) { + auto entityID = item.key(); + auto count = item.value(); + + auto entityScriptingInterface = DependencyManager::get(); + auto properties = entityScriptingInterface->getEntityProperties(entityID, EntityPropertyFlags("dimensions")); + auto dimensions = properties.getDimensions(); + const glm::vec3 DEFAULT_DIMENSIONS = glm::vec3(0.1f, 0.1f, 0.1f); + if (dimensions != DEFAULT_DIMENSIONS) { + // Entity has been auto-resized; adjust dimensions if it seems too big. + + const float RESCALE_THRESHOLD = 10.0f; // Resize entities larger than this as the FBX is likely in cm or mm. + if (dimensions.x > RESCALE_THRESHOLD || dimensions.y > RESCALE_THRESHOLD || dimensions.z > RESCALE_THRESHOLD) { + dimensions *= 0.1f; + EntityItemProperties properties; + properties.setDimensions(dimensions); + properties.setCollisionless(false); // Reset to default. + entityScriptingInterface->editEntity(entityID, properties); + qInfo() << "Asset auto-resized"; + } + + item = _addAssetToWorldResizeList.erase(item); // Finished with this entity. + + } else { + // Increment count of checks done. + _addAssetToWorldResizeList[entityID]++; + + const int CHECK_MODEL_SIZE_MAX_CHECKS = 10; + if (_addAssetToWorldResizeList[entityID] > CHECK_MODEL_SIZE_MAX_CHECKS) { + // Have done enough checks; model was either the default size or something's gone wrong. + + EntityItemProperties properties; + properties.setCollisionless(false); // Reset to default. + entityScriptingInterface->editEntity(entityID, properties); + + item = _addAssetToWorldResizeList.erase(item); // Finished with this entity. + + } else { + // No action on this entity; advance to next. + ++item; + } + } + } + + // Stop timer if nothing in list to check. + if (_addAssetToWorldResizeList.size() == 0) { + _addAssetToWorldTimer.stop(); + } +} + void Application::handleUnzip(QString filePath, bool autoAdd) { if (autoAdd && !filePath.isEmpty()) { addAssetToWorld(filePath); diff --git a/interface/src/Application.h b/interface/src/Application.h index 85b8873282..6d3ae90e23 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -320,6 +320,8 @@ public slots: void addAssetToWorldUpload(QString path, QString mapping); void addAssetToWorldSetMapping(QString mapping, QString hash); void addAssetToWorldAddEntity(QString mapping); + void addAssetToWorldCheckModelSize(); + void handleUnzip(QString filePath = "", bool autoAdd = false); FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; } @@ -633,6 +635,8 @@ private: QQuickItem* _addAssetToWorldMessageBox{ nullptr }; void addAssetToWorldError(QString errorText); + QTimer _addAssetToWorldTimer; + QHash _addAssetToWorldResizeList; FileScriptingInterface* _fileDownload; }; From 5313a682ad765b5a09b42c16c0db455a63bedf3d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 8 Dec 2016 19:48:51 +1300 Subject: [PATCH 059/101] Fix compiler warning --- interface/src/Application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 35e1e87303..df7f551e0e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5686,7 +5686,6 @@ void Application::addAssetToWorldCheckModelSize() { auto item = _addAssetToWorldResizeList.begin(); while (item != _addAssetToWorldResizeList.end()) { auto entityID = item.key(); - auto count = item.value(); auto entityScriptingInterface = DependencyManager::get(); auto properties = entityScriptingInterface->getEntityProperties(entityID, EntityPropertyFlags("dimensions")); From 4c236b293ed97020cfdcf2805ab7e519c4db7fbc Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 9 Dec 2016 01:47:40 +1300 Subject: [PATCH 060/101] First pass at controller interaction with 3D Web overlay --- interface/src/Application.cpp | 4 + interface/src/Application.h | 1 + interface/src/ui/overlays/Overlays.cpp | 32 +++ interface/src/ui/overlays/Overlays.h | 13 + .../system/controllers/handControllerGrab.js | 235 ++++++++++++++++-- 5 files changed, 267 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index df7f551e0e..f4c55abb9c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3896,6 +3896,10 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { } } +unsigned int Application::getKeyboardFocusOverlay() { + return _keyboardFocusedOverlay.get(); +} + void Application::setKeyboardFocusOverlay(unsigned int overlayID) { if (overlayID != _keyboardFocusedOverlay.get()) { _keyboardFocusedOverlay.set(overlayID); diff --git a/interface/src/Application.h b/interface/src/Application.h index 6d3ae90e23..eb461c9ae0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -374,6 +374,7 @@ public slots: void setKeyboardFocusEntity(QUuid id); void setKeyboardFocusEntity(EntityItemID entityItemID); + unsigned int getKeyboardFocusOverlay(); void setKeyboardFocusOverlay(unsigned int overlayID); private slots: diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index c65dddb4ab..3226a2852e 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -604,6 +604,38 @@ bool Overlays::isAddedOverlay(unsigned int id) { return _overlaysHUD.contains(id) || _overlaysWorld.contains(id); } +void Overlays::sendMousePressOnOverlay(unsigned int overlayID, const PointerEvent& event) { + emit mousePressOnOverlay(overlayID, event); +} + +void Overlays::sendMouseReleaseOnOverlay(unsigned int overlayID, const PointerEvent& event) { + emit mouseReleaseOnOverlay(overlayID, event); +} + +void Overlays::sendMouseMoveOnOverlay(unsigned int overlayID, const PointerEvent& event) { + emit mouseMoveOnOverlay(overlayID, event); +} + +void Overlays::sendHoverEnterOverlay(unsigned int id, PointerEvent event) { + emit hoverEnterOverlay(id, event); +} + +void Overlays::sendHoverOverOverlay(unsigned int id, PointerEvent event) { + emit hoverOverOverlay(id, event); +} + +void Overlays::sendHoverLeaveOverlay(unsigned int id, PointerEvent event) { + emit hoverLeaveOverlay(id, event); +} + +unsigned int Overlays::getKeyboardFocusOverlay() const { + return qApp->getKeyboardFocusOverlay(); +} + +void Overlays::setKeyboardFocusOverlay(unsigned int id) { + qApp->setKeyboardFocusOverlay(id); +} + float Overlays::width() const { auto offscreenUi = DependencyManager::get(); return offscreenUi->getWindow()->size().width(); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 5ca4f36a30..c05a634344 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -82,6 +82,8 @@ const unsigned int UNKNOWN_OVERLAY_ID = 0; class Overlays : public QObject { Q_OBJECT + Q_PROPERTY(unsigned int keyboardFocusOverlay READ getKeyboardFocusOverlay WRITE setKeyboardFocusOverlay) + public: Overlays(); @@ -256,6 +258,17 @@ public slots: /// return true if there is a panel with that id else false bool isAddedPanel(unsigned int id) { return _panels.contains(id); } + void sendMousePressOnOverlay(unsigned int overlayID, const PointerEvent& event); + void sendMouseReleaseOnOverlay(unsigned int overlayID, const PointerEvent& event); + void sendMouseMoveOnOverlay(unsigned int overlayID, const PointerEvent& event); + + void sendHoverEnterOverlay(unsigned int id, PointerEvent event); + void sendHoverOverOverlay(unsigned int id, PointerEvent event); + void sendHoverLeaveOverlay(unsigned int id, PointerEvent event); + + unsigned int getKeyboardFocusOverlay() const; + void setKeyboardFocusOverlay(unsigned int id); + signals: /**jsdoc * Emitted when an overlay is deleted diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index ff1407cbf0..3c448d2265 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -186,6 +186,7 @@ var STATE_NEAR_TRIGGER = 4; var STATE_FAR_TRIGGER = 5; var STATE_HOLD = 6; var STATE_ENTITY_TOUCHING = 7; +var STATE_OVERLAY_TOUCHING = 8; var holdEnabled = true; var nearGrabEnabled = true; @@ -208,6 +209,8 @@ var mostRecentSearchingHand = RIGHT_HAND; var DEFAULT_SPHERE_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/equip-Fresnel-3.fbx"; +var HARDWARE_MOUSE_ID = 0; // Value reserved for hardware mouse. + CONTROLLER_STATE_MACHINE[STATE_OFF] = { name: "off", enterMethod: "offEnter", @@ -249,6 +252,12 @@ CONTROLLER_STATE_MACHINE[STATE_ENTITY_TOUCHING] = { exitMethod: "entityTouchingExit", updateMethod: "entityTouching" }; +CONTROLLER_STATE_MACHINE[STATE_OVERLAY_TOUCHING] = { + name: "overlayTouching", + enterMethod: "overlayTouchingEnter", + exitMethod: "overlayTouchingExit", + updateMethod: "overlayTouching" +}; function distanceBetweenPointAndEntityBoundingBox(point, entityProps) { var entityXform = new Xform(entityProps.rotation, entityProps.position); @@ -273,27 +282,40 @@ function angleBetween(a, b) { return Math.acos(Vec3.dot(Vec3.normalize(a), Vec3.normalize(b))); } -function projectOntoEntityXYPlane(entityID, worldPos) { - var props = entityPropertiesCache.getProps(entityID); - var invRot = Quat.inverse(props.rotation); - var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, props.position)); - var invDimensions = { x: 1 / props.dimensions.x, - y: 1 / props.dimensions.y, - z: 1 / props.dimensions.z }; - var normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); - return { x: normalizedPos.x * props.dimensions.x, - y: (1 - normalizedPos.y) * props.dimensions.y }; // flip y-axis +function projectOntoXYPlane(worldPos, position, rotation, dimensions, registrationPoint) { + var invRot = Quat.inverse(rotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, position)); + var invDimensions = { x: 1 / dimensions.x, + y: 1 / dimensions.y, + z: 1 / dimensions.z }; + var normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), registrationPoint); + return { x: normalizedPos.x * dimensions.x, + y: (1 - normalizedPos.y) * dimensions.y }; // flip y-axis } -function handLaserIntersectEntity(entityID, start) { +function projectOntoEntityXYPlane(entityID, worldPos) { + var props = entityPropertiesCache.getProps(entityID); + return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint); +} + +function projectOntoOverlayXYPlane(overlayID, worldPos) { + var position = Overlays.getProperty(overlayID, "position"); + var rotation = Overlays.getProperty(overlayID, "rotation"); + var dimensions = Overlays.getProperty(overlayID, "dimensions"); + if (!dimensions.z) { + dimensions.z = 0; + } + var registrationPoint = { x: 0.5, y: 0.5, z: 0.5 }; + return projectOntoXYPlane(worldPos, position, rotation, dimensions, registrationPoint); +} + +function handLaserIntersectItem(position, rotation, start) { var worldHandPosition = start.position; var worldHandRotation = start.orientation; - var props = entityPropertiesCache.getProps(entityID); - - if (props.position) { - var planePosition = props.position; - var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); + if (position) { + var planePosition = position; + var planeNormal = Vec3.multiplyQbyV(rotation, {x: 0, y: 0, z: 1.0}); var rayStart = worldHandPosition; var rayDirection = Quat.getUp(worldHandRotation); var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); @@ -318,6 +340,17 @@ function handLaserIntersectEntity(entityID, start) { } } +function handLaserIntersectEntity(entityID, start) { + var props = entityPropertiesCache.getProps(entityID); + return handLaserIntersectItem(props.position, props.rotation, start); +} + +function handLaserIntersectOverlay(overlayID, start) { + var position = Overlays.getProperty(overlayID, "position"); + var rotation = Overlays.getProperty(overlayID, "rotation"); + return handLaserIntersectItem(position, rotation, start); +} + function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal); if (rayDirectionDotPlaneNormal > 0.00001 || rayDirectionDotPlaneNormal < -0.00001) { @@ -725,6 +758,7 @@ function MyController(hand) { this.actionID = null; // action this script created... this.grabbedEntity = null; // on this entity. + this.grabbedOverlay = null; this.state = STATE_OFF; this.pointer = null; // entity-id of line object this.entityActivated = false; @@ -1152,6 +1186,7 @@ function MyController(hand) { var result = { entityID: null, + overlayID: null, searchRay: pickRay, distance: PICK_MAX_DISTANCE }; @@ -1417,6 +1452,7 @@ function MyController(hand) { var name; this.grabbedEntity = null; + this.grabbedOverlay = null; this.isInitialGrab = false; this.shouldResetParentOnRelease = false; @@ -1529,7 +1565,8 @@ function MyController(hand) { // send mouse events for button highlights and tooltips. if (this.hand == mostRecentSearchingHand || (this.hand !== mostRecentSearchingHand && this.getOtherHandController().state !== STATE_SEARCHING && - this.getOtherHandController().state !== STATE_ENTITY_TOUCHING)) { + this.getOtherHandController().state !== STATE_ENTITY_TOUCHING && + this.getOtherHandController().state !== STATE_OVERLAY_TOUCHING)) { // most recently searching hand has priority over other hand, for the purposes of button highlighting. pointerEvent = { @@ -1582,6 +1619,64 @@ function MyController(hand) { } } + var overlay; + + if (rayPickInfo.overlayID) { + overlay = rayPickInfo.overlayID; + + if (Overlays.keyboardFocusOverlay != overlay) { + Overlays.keyboardFocusOverlay = overlay; + + pointerEvent = { + type: "Move", + id: HARDWARE_MOUSE_ID, + pos2D: projectOntoOverlayXYPlane(overlay, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + + this.hoverOverlay = overlay; + Overlays.sendHoverEnterOverlay(overlay, pointerEvent); + } + + // Send mouse events for button highlights and tooltips. + if (this.hand == mostRecentSearchingHand || (this.hand !== mostRecentSearchingHand && + this.getOtherHandController().state !== STATE_SEARCHING && + this.getOtherHandController().state !== STATE_ENTITY_TOUCHING && + this.getOtherHandController().state !== STATE_OVERLAY_TOUCHING)) { + + // most recently searching hand has priority over other hand, for the purposes of button highlighting. + pointerEvent = { + type: "Move", + id: HARDWARE_MOUSE_ID, + pos2D: projectOntoOverlayXYPlane(overlay, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + + Overlays.sendMouseMoveOnOverlay(overlay, pointerEvent); + Overlays.sendHoverOverOverlay(overlay, pointerEvent); + } + + if (this.triggerSmoothedGrab() && !isEditing()) { + this.grabbedOverlay = overlay; + this.setState(STATE_OVERLAY_TOUCHING, "begin touching overlay '" + overlay + "'"); + return; + } + + } else if (this.hoverOverlay) { + pointerEvent = { + type: "Move", + id: HARDWARE_MOUSE_ID + }; + Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent); + this.hoverOverlay = null; + } + this.updateEquipHaptics(potentialEquipHotspot, handPosition); var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); @@ -2286,7 +2381,6 @@ function MyController(hand) { Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent); Entities.sendHoverLeaveEntity(this.grabbedEntity, pointerEvent); } - this.focusedEntity = null; }; this.entityTouching = function(dt) { @@ -2340,6 +2434,110 @@ function MyController(hand) { } }; + this.overlayTouchingEnter = function () { + // Test for intersection between controller laser and Web overlay plane. + var intersectInfo = + handLaserIntersectOverlay(this.grabbedOverlay, getControllerWorldLocation(this.handToController(), true)); + if (intersectInfo) { + var pointerEvent = { + type: "Press", + id: HARDWARE_MOUSE_ID, + pos2D: projectOntoOverlayXYPlane(this.grabbedOverlay, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary", + isPrimaryHeld: true + }; + + // TODO: post2D isn't calculated correctly; use dummy values to prevent crash. + pointerEvent.pos2D.x = 50; + pointerEvent.pos2D.y = 50; + + Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); + + this.touchingEnterTimer = 0; + this.touchingEnterPointerEvent = pointerEvent; + this.touchingEnterPointerEvent.button = "None"; + this.deadspotExpired = false; + } + }; + + this.overlayTouchingExit = function () { + // Test for intersection between controller laser and Web overlay plane. + var intersectInfo = + handLaserIntersectOverlay(this.grabbedOverlay, getControllerWorldLocation(this.handToController(), true)); + if (intersectInfo) { + var pointerEvent; + if (this.deadspotExpired) { + pointerEvent = { + type: "Release", + id: HARDWARE_MOUSE_ID, + pos2D: projectOntoOverlayXYPlane(this.grabbedOverlay, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary" + }; + } else { + pointerEvent = this.touchingEnterPointerEvent; + pointerEvent.type = "Release"; + pointerEvent.button = "Primary"; + pointerEvent.isPrimaryHeld = false; + } + + Overlays.sendMouseReleaseOnOverlay(this.grabbedOverlay, pointerEvent); + Overlays.sendHoverLeaveOverlay(this.grabbedOverlay, pointerEvent); + } + }; + + this.overlayTouching = function (dt) { + this.touchingEnterTimer += dt; + + if (!this.triggerSmoothedGrab()) { + this.setState(STATE_OFF, "released trigger"); + return; + } + + // Test for intersection between controller laser and Web overlay plane. + var intersectInfo = + handLaserIntersectOverlay(this.grabbedOverlay, getControllerWorldLocation(this.handToController(), true)); + if (intersectInfo) { + + if (Overlays.keyboardFocusOverlay != this.grabbedOverlay) { + Overlays.keyboardFocusOverlay = this.grabbedOverlay; + } + + var pointerEvent = { + type: "Move", + id: HARDWARE_MOUSE_ID, + pos2D: projectOntoOverlayXYPlane(this.grabbedOverlay, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "NoButtons", + isPrimaryHeld: true + }; + + var POINTER_PRESS_TO_MOVE_DELAY = 0.15; // seconds + var POINTER_PRESS_TO_MOVE_DEADSPOT_ANGLE = 0.05; // radians ~ 3 degrees + if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || + angleBetween(pointerEvent.direction, this.touchingEnterPointerEvent.direction) > POINTER_PRESS_TO_MOVE_DEADSPOT_ANGLE) { + Overlays.sendMouseMoveOnOverlay(this.grabbedOverlay, pointerEvent); + this.deadspotExpired = true; + } + + this.intersectionDistance = intersectInfo.distance; + if (farGrabEnabled) { + this.searchIndicatorOn(intersectInfo.searchRay); + } + Reticle.setVisible(false); + } else { + this.setState(STATE_OFF, "grabbed overlay was destroyed"); + return; + } + }; + this.release = function() { this.turnOffVisualizations(); @@ -2381,6 +2579,7 @@ function MyController(hand) { this.actionID = null; this.grabbedEntity = null; + this.grabbedOverlay = null; this.grabbedHotspot = null; if (this.triggerSmoothedGrab()) { From 1213a1dfd1ffac7505cb4b9eb14c6eb9c42a492c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 9 Dec 2016 12:41:54 +1300 Subject: [PATCH 061/101] Fix Overlays.getProperty method not returning value to script --- interface/src/ui/overlays/Overlays.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 3226a2852e..1ca261914f 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -396,7 +396,7 @@ QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const Ove if (!value.value.isValid()) { return QScriptValue::UndefinedValue; } - return engine->newVariant(value.value); + return engine->toScriptValue(value.value); } void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value) { From 7d8926c802ac6af46b2efab546ed2d574674104d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 9 Dec 2016 17:00:00 +1300 Subject: [PATCH 062/101] Fix Web overlay x, y coordinate calcs --- interface/src/ui/overlays/Web3DOverlay.cpp | 3 +++ .../system/controllers/handControllerGrab.js | 25 +++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index fa04cd706a..9ef089758d 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -312,6 +312,9 @@ QVariant Web3DOverlay::getProperty(const QString& property) { if (property == "scriptURL") { return _scriptURL; } + if (property == "resolution") { + return vec2toVariant(_resolution); + } if (property == "dpi") { return _dpi; } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 3c448d2265..427f98200e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -140,6 +140,10 @@ var ONE_VEC = { var NULL_UUID = "{00000000-0000-0000-0000-000000000000}"; +var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 }; +var INCHES_TO_METERS = 1.0 / 39.3701; + + // these control how long an abandoned pointer line or action will hang around var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; @@ -301,12 +305,20 @@ function projectOntoEntityXYPlane(entityID, worldPos) { function projectOntoOverlayXYPlane(overlayID, worldPos) { var position = Overlays.getProperty(overlayID, "position"); var rotation = Overlays.getProperty(overlayID, "rotation"); - var dimensions = Overlays.getProperty(overlayID, "dimensions"); - if (!dimensions.z) { - dimensions.z = 0; + var dimensions; + + var dpi = Overlays.getProperty(overlayID, "dpi"); + if (dpi) { + // Calculate physical dimensions for web3d overlay; "dimensions" property is used as a scale. + var resolution = Overlays.getProperty(overlayID, "resolution"); + resolution.z = 1; // Circumvent divide-by-zero. + var scale = Overlays.getProperty(overlayID, "dimensions"); + dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); + } else { + dimensions = Overlays.getProperty(overlayID, "dimensions"); } - var registrationPoint = { x: 0.5, y: 0.5, z: 0.5 }; - return projectOntoXYPlane(worldPos, position, rotation, dimensions, registrationPoint); + + return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); } function handLaserIntersectItem(position, rotation, start) { @@ -2450,9 +2462,6 @@ function MyController(hand) { isPrimaryHeld: true }; - // TODO: post2D isn't calculated correctly; use dummy values to prevent crash. - pointerEvent.pos2D.x = 50; - pointerEvent.pos2D.y = 50; Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); From bad8d20dd910b25327a0e1635a20d2fc0341abf3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 9 Dec 2016 17:02:06 +1300 Subject: [PATCH 063/101] Rescale large Clara.io models to 1% instead of 10% --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f4c55abb9c..df552369d8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5700,7 +5700,7 @@ void Application::addAssetToWorldCheckModelSize() { const float RESCALE_THRESHOLD = 10.0f; // Resize entities larger than this as the FBX is likely in cm or mm. if (dimensions.x > RESCALE_THRESHOLD || dimensions.y > RESCALE_THRESHOLD || dimensions.z > RESCALE_THRESHOLD) { - dimensions *= 0.1f; + dimensions *= 0.01f; EntityItemProperties properties; properties.setDimensions(dimensions); properties.setCollisionless(false); // Reset to default. From e956608fa469c373338d4d1a5190d7d00883486c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 9 Dec 2016 18:31:52 +1300 Subject: [PATCH 064/101] Fix keyboard focus when switch highlight between overlays and entities --- interface/src/Application.cpp | 2 ++ scripts/system/controllers/handControllerGrab.js | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index df552369d8..e7b44c6316 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1147,6 +1147,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto entityScriptingInterface = DependencyManager::get(); connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, [this](const EntityItemID& entityItemID, const PointerEvent& event) { + setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); setKeyboardFocusEntity(entityItemID); }); @@ -1165,6 +1166,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto overlays = &(qApp->getOverlays()); connect(overlays, &Overlays::mousePressOnOverlay, [=](unsigned int overlayID, const PointerEvent& event) { + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); setKeyboardFocusOverlay(overlayID); }); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 427f98200e..fabd7cfdfe 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1558,6 +1558,7 @@ function MyController(hand) { name = entityPropertiesCache.getProps(entity).name; if (Entities.keyboardFocusEntity != entity) { + Overlays.keyboardFocusOverlay = 0; Entities.keyboardFocusEntity = entity; pointerEvent = { @@ -1637,6 +1638,7 @@ function MyController(hand) { overlay = rayPickInfo.overlayID; if (Overlays.keyboardFocusOverlay != overlay) { + Entities.keyboardFocusEntity = null; Overlays.keyboardFocusOverlay = overlay; pointerEvent = { @@ -2412,6 +2414,7 @@ function MyController(hand) { if (intersectInfo) { if (Entities.keyboardFocusEntity != this.grabbedEntity) { + Overlays.keyboardFocusOverlay = 0; Entities.keyboardFocusEntity = this.grabbedEntity; } @@ -2514,6 +2517,7 @@ function MyController(hand) { if (intersectInfo) { if (Overlays.keyboardFocusOverlay != this.grabbedOverlay) { + Entities.keyboardFocusEntity = null; Overlays.keyboardFocusOverlay = this.grabbedOverlay; } From c87149cfc2d59c69dd22d6807fedcdf5f81f41c0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 10 Dec 2016 12:59:01 +1300 Subject: [PATCH 065/101] Tidying --- libraries/entities/src/EntityItemProperties.h | 2 +- scripts/system/html/js/marketplacesInject.js | 6 ++++++ scripts/system/libraries/WebTablet.js | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 5a812ffe37..fb08182a2e 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -316,7 +316,7 @@ private: float _localRenderAlpha; bool _localRenderAlphaChanged; bool _defaultSettings; - bool _dimensionsInitialized = true; // Only false if creating an entity localy with no dimensions properties + bool _dimensionsInitialized = true; // Only false if creating an entity locally with no dimensions properties // NOTE: The following are pseudo client only properties. They are only used in clients which can access // properties of model geometry. But these properties are not serialized like other properties. diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 401185f290..12a5d033d0 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -82,6 +82,7 @@ // Clara library page. if (location.href.indexOf("clara.io/library") !== -1) { + // Make entries navigate to "Image" view instead of default "Real Time" view. var elements = $("a.thumbnail"); for (var i = 0, length = elements.length; i < length; i++) { var value = elements[i].getAttribute("href"); @@ -93,12 +94,15 @@ // Clara item page. if (location.href.indexOf("clara.io/view/") !== -1) { + // Make site navigation links retain gameCheck etc. parameters. var element = $("a[href^=\'/library\']")[0]; var parameters = "?gameCheck=true&public=true"; var href = element.getAttribute("href"); if (href.slice(-parameters.length) !== parameters) { element.setAttribute("href", href + parameters); } + + // Replace download options with a single, "Download to High Fidelity" option. var buttons = $("a.embed-button").parent("div"); if (buttons.length > 0) { var downloadFBX = buttons.find("a[data-extension=\'fbx\']")[0]; @@ -109,6 +113,8 @@ downloadFBX.innerHTML = " Download to High Fidelity"; buttons.children(":nth-child(2), .btn-group , .embed-button").each(function () { this.remove(); }); } + + // Automatic download to High Fidelity. var downloadTimer; function startAutoDownload() { if (!downloadTimer) { diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index fa2a8a02d7..462ff974a2 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -49,9 +49,9 @@ WebTablet = function (url, width, dpi, clientOnly) { var DPI = dpi || DEFAULT_DPI; var spawnInfo = calcSpawnInfo(); - var tabletEntityPosition = spawnInfo.position; var tabletEntityRotation = spawnInfo.rotation; + this.tabletEntityID = Entities.addEntity({ name: "tablet", type: "Model", From 4f0c429e7f56418457acf624bf86a7844b1c0f85 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 11 Dec 2016 17:08:36 +1300 Subject: [PATCH 066/101] Fix model auto-scaling to 1% not being seen by observers --- interface/src/Application.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ffb2269ff0..80f935f077 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5711,7 +5711,15 @@ void Application::addAssetToWorldCheckModelSize() { EntityItemProperties properties; properties.setDimensions(dimensions); properties.setCollisionless(false); // Reset to default. - entityScriptingInterface->editEntity(entityID, properties); + + // Edit entity via invokeMethod() to ensure that observers get this resize after the auto-resize edit performed + // in RenderableModelEntityItem::update(). + //entityScriptingInterface->editEntity(entityID, properties); + QMetaObject::invokeMethod(DependencyManager::get().data(), "editEntity", + Qt::QueuedConnection, + Q_ARG(QUuid, entityID), + Q_ARG(EntityItemProperties, properties)); + qInfo() << "Asset auto-resized"; } From 14ecb2498612a90d56c6b79de79b3afcd73a0503 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 11 Dec 2016 17:43:55 +1300 Subject: [PATCH 067/101] Remove extra browse history item created by Clara download --- scripts/system/html/js/marketplacesInject.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 12a5d033d0..96fa1c1faa 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -117,6 +117,7 @@ // Automatic download to High Fidelity. var downloadTimer; function startAutoDownload() { + window.scrollTo(0, 0); // Scroll to top ready for history.back(). if (!downloadTimer) { downloadTimer = setInterval(autoDownload, 1000); } @@ -131,6 +132,7 @@ EventBridge.emitWebEvent("CLARA.IO DOWNLOAD " + href); console.log("Clara.io FBX file download initiated for " + href); $("a.btn.cancel").click(); + history.back(); // Remove history item created by clicking "download". }; } else { clearInterval(downloadTimer); From 8a399c7cc0c2bc9d6cc2d18b75684d708d621aaa Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 12 Dec 2016 17:01:02 +1300 Subject: [PATCH 068/101] Fix model auto-scaling to 1% not being seen by observers take 2 --- interface/src/Application.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 80f935f077..10486afd79 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5662,12 +5662,14 @@ void Application::addAssetToWorldAddEntity(QString mapping) { properties.setModelURL("atp:" + mapping); properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); properties.setCollisionless(true); // Temporarily set so that doesn't collide with avatar. + properties.setVisible(false); // Temporarily set so that don't see at large unresized dimensions. properties.setDynamic(false); properties.setPosition(getMyAvatar()->getPosition() + getMyAvatar()->getOrientation() * glm::vec3(0.0f, 0.0f, -2.0f)); properties.setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); auto entityID = DependencyManager::get()->addEntity(properties); + // Note: Model dimensions are not available here; model is scaled per FBX mesh in RenderableModelEntityItem::update() later - // on. But FBX dimensions may be in cm or mm, so we monitor for the dimension change and rescale again if warranted. + // on. But FBX dimensions may be in cm, so we monitor for the dimension change and rescale again if warranted. if (entityID == QUuid()) { QString errorInfo = "Could not add asset " + mapping + " to world."; @@ -5710,16 +5712,10 @@ void Application::addAssetToWorldCheckModelSize() { dimensions *= 0.01f; EntityItemProperties properties; properties.setDimensions(dimensions); - properties.setCollisionless(false); // Reset to default. - - // Edit entity via invokeMethod() to ensure that observers get this resize after the auto-resize edit performed - // in RenderableModelEntityItem::update(). - //entityScriptingInterface->editEntity(entityID, properties); - QMetaObject::invokeMethod(DependencyManager::get().data(), "editEntity", - Qt::QueuedConnection, - Q_ARG(QUuid, entityID), - Q_ARG(EntityItemProperties, properties)); - + properties.setVisible(true); + properties.setCollisionless(false); + properties.setLastEdited(usecTimestampNow()); + entityScriptingInterface->editEntity(entityID, properties); qInfo() << "Asset auto-resized"; } @@ -5734,7 +5730,9 @@ void Application::addAssetToWorldCheckModelSize() { // Have done enough checks; model was either the default size or something's gone wrong. EntityItemProperties properties; - properties.setCollisionless(false); // Reset to default. + properties.setVisible(true); + properties.setCollisionless(false); + properties.setLastEdited(usecTimestampNow()); entityScriptingInterface->editEntity(entityID, properties); item = _addAssetToWorldResizeList.erase(item); // Finished with this entity. From efb784f7037c5706d18f13ad131cbdc79b03af7a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 13 Dec 2016 13:11:21 +1300 Subject: [PATCH 069/101] Code review --- interface/src/Application.cpp | 78 ++++++++++--------- interface/src/ui/overlays/Image3DOverlay.cpp | 5 +- interface/src/ui/overlays/Overlays.h | 4 +- interface/src/ui/overlays/Web3DOverlay.cpp | 6 +- libraries/networking/src/ResourceRequest.cpp | 2 +- .../system/controllers/handControllerGrab.js | 2 +- scripts/system/libraries/WebTablet.js | 3 +- 7 files changed, 49 insertions(+), 51 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 10486afd79..7f61c80b69 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2332,48 +2332,47 @@ bool Application::event(QEvent* event) { { if (!_keyboardFocusedEntity.get().isInvalidID()) { switch (event->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease: { - auto entityScriptingInterface = DependencyManager::get(); - auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get()); - if (entity && entity->getEventHandler()) { - event->setAccepted(false); - QCoreApplication::sendEvent(entity->getEventHandler(), event); - if (event->isAccepted()) { - _lastAcceptedKeyPress = usecTimestampNow(); - return true; + case QEvent::KeyPress: + case QEvent::KeyRelease: { + //auto entityScriptingInterface = DependencyManager::get(); + auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get()); + if (entity && entity->getEventHandler()) { + event->setAccepted(false); + QCoreApplication::sendEvent(entity->getEventHandler(), event); + if (event->isAccepted()) { + _lastAcceptedKeyPress = usecTimestampNow(); + return true; + } } + break; + } + default: + break; } - break; - } - - default: - break; - } } } { if (_keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) { switch (event->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease: { - // Only Web overlays can have focus. - auto overlay = - std::dynamic_pointer_cast(getOverlays().getOverlay(_keyboardFocusedOverlay.get())); - if (overlay && overlay->getEventHandler()) { - event->setAccepted(false); - QCoreApplication::sendEvent(overlay->getEventHandler(), event); - if (event->isAccepted()) { - _lastAcceptedKeyPress = usecTimestampNow(); - return true; + case QEvent::KeyPress: + case QEvent::KeyRelease: { + // Only Web overlays can have focus. + auto overlay = + std::dynamic_pointer_cast(getOverlays().getOverlay(_keyboardFocusedOverlay.get())); + if (overlay && overlay->getEventHandler()) { + event->setAccepted(false); + QCoreApplication::sendEvent(overlay->getEventHandler(), event); + if (event->isAccepted()) { + _lastAcceptedKeyPress = usecTimestampNow(); + return true; + } } + break; + } + default: + break; } - break; - } - default: - break; - } } } @@ -3874,6 +3873,8 @@ void Application::setKeyboardFocusEntity(QUuid id) { setKeyboardFocusEntity(entityItemID); } +static const float FOCUS_HIGHLIGHT_EXPANSION_FACTOR = 1.05f; + void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { if (_keyboardFocusedEntity.get() != entityItemID) { _keyboardFocusedEntity.set(entityItemID); @@ -3897,7 +3898,8 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { } _lastAcceptedKeyPress = usecTimestampNow(); - setKeyboardFocusHighlight(entity->getPosition(), entity->getRotation(), entity->getDimensions() * 1.05f); + setKeyboardFocusHighlight(entity->getPosition(), entity->getRotation(), + entity->getDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR); } } } @@ -3930,7 +3932,7 @@ void Application::setKeyboardFocusOverlay(unsigned int overlayID) { } _lastAcceptedKeyPress = usecTimestampNow(); - auto size = overlay->getSize() * 1.05f; + auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR; const float OVERLAY_DEPTH = 0.0105f; setKeyboardFocusHighlight(overlay->getPosition(), overlay->getRotation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH)); } @@ -5508,9 +5510,9 @@ void Application::addAssetToWorldFromURLRequestFinished() { temporaryDir.setAutoRemove(false); if (temporaryDir.isValid()) { QString temporaryDirPath = temporaryDir.path(); - QString filename = url.section("filename=", 1, 1); + QString filename = url.section("filename=", 1, 1); // Filename from trailing "?filename=" URL parameter. QString downloadPath = temporaryDirPath + "/" + filename; - qInfo() << "Download path:" << downloadPath; + qInfo(interfaceapp) << "Download path:" << downloadPath; QFile tempFile(downloadPath); if (tempFile.open(QIODevice::WriteOnly)) { @@ -5684,7 +5686,7 @@ void Application::addAssetToWorldAddEntity(QString mapping) { // Inform user. QString successInfo = "Asset " + mapping.mid(1) + " added to world."; - qInfo() << "Downloading asset completed: " + successInfo; + qInfo(interfaceapp) << "Downloading asset completed: " + successInfo; _addAssetToWorldMessageBox->setProperty("text", successInfo); _addAssetToWorldMessageBox->setProperty("buttons", QMessageBox::Ok); _addAssetToWorldMessageBox->setProperty("defaultButton", QMessageBox::Ok); @@ -5716,7 +5718,7 @@ void Application::addAssetToWorldCheckModelSize() { properties.setCollisionless(false); properties.setLastEdited(usecTimestampNow()); entityScriptingInterface->editEntity(entityID, properties); - qInfo() << "Asset auto-resized"; + qInfo(interfaceapp) << "Asset auto-resized"; } item = _addAssetToWorldResizeList.erase(item); // Finished with this entity. diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 37f36d5e34..45d63d9cf1 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -202,9 +202,8 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. Transform transform = getTransform(); - // XXX this code runs too often for this... - // applyTransformTo(transform, true); - // setTransform(transform); + + // Don't call applyTransformTo() or setTransform() here because this code runs too frequently. // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index c05a634344..90644206ee 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -305,8 +305,8 @@ private: PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); - unsigned int _currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID; - unsigned int _currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID; + unsigned int _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; + unsigned int _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID }; }; #endif // hifi_Overlays_h diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 9ef089758d..da7c67755a 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -346,11 +346,7 @@ glm::vec2 Web3DOverlay::getSize() { bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { // FIXME - face and surfaceNormal not being returned - // Make sure position and rotation is updated. - // XXX this code runs too often for this... - //Transform transform = getTransform(); - //applyTransformTo(transform, true); - //setTransform(transform); + // Don't call applyTransformTo() or setTransform() here because this code runs too frequently. // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), getSize(), distance); diff --git a/libraries/networking/src/ResourceRequest.cpp b/libraries/networking/src/ResourceRequest.cpp index a374763fcf..aeeab2232a 100644 --- a/libraries/networking/src/ResourceRequest.cpp +++ b/libraries/networking/src/ResourceRequest.cpp @@ -34,7 +34,7 @@ QString ResourceRequest::getResultString() const { case Timeout: return "Timeout"; case ServerUnavailable: return "Server Unavailable"; case AccessDenied: return "Access Denied"; - case InvalidURL: return "Ivalid URL"; + case InvalidURL: return "Invalid URL"; case NotFound: return "Not Found"; default: return "Unspecified Error"; } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index fabd7cfdfe..69f685794e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -309,7 +309,7 @@ function projectOntoOverlayXYPlane(overlayID, worldPos) { var dpi = Overlays.getProperty(overlayID, "dpi"); if (dpi) { - // Calculate physical dimensions for web3d overlay; "dimensions" property is used as a scale. + // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale. var resolution = Overlays.getProperty(overlayID, "resolution"); resolution.z = 1; // Circumvent divide-by-zero. var scale = Overlays.getProperty(overlayID, "dimensions"); diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 462ff974a2..72be0e583e 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -47,6 +47,7 @@ WebTablet = function (url, width, dpi, clientOnly) { var HEIGHT = WIDTH * ASPECT * TABLET_HEIGHT_SCALE; var DEPTH = 0.025; var DPI = dpi || DEFAULT_DPI; + var SENSOR_TO_ROOM_MATRIX = -2; var spawnInfo = calcSpawnInfo(); var tabletEntityPosition = spawnInfo.position; @@ -63,7 +64,7 @@ WebTablet = function (url, width, dpi, clientOnly) { }), dimensions: {x: WIDTH, y: HEIGHT, z: DEPTH}, parentID: MyAvatar.sessionUUID, - parentJointIndex: -1 + parentJointIndex: SENSOR_TO_ROOM_MATRIX }, clientOnly); var WEB_OVERLAY_Z_OFFSET = -0.01; From aefa116207360c48e40b4b907d40d8d7e42cf7de Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 13 Dec 2016 17:55:22 +1300 Subject: [PATCH 070/101] Add extra logging for Clara.io asset resizing --- interface/src/Application.cpp | 15 +++++++++++---- .../src/RenderableModelEntityItem.cpp | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ec0665d178..e62a25a53a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5746,23 +5746,29 @@ void Application::addAssetToWorldCheckModelSize() { while (item != _addAssetToWorldResizeList.end()) { auto entityID = item.key(); + EntityPropertyFlags propertyFlags; + propertyFlags += PROP_NAME; + propertyFlags += PROP_DIMENSIONS; auto entityScriptingInterface = DependencyManager::get(); - auto properties = entityScriptingInterface->getEntityProperties(entityID, EntityPropertyFlags("dimensions")); + auto properties = entityScriptingInterface->getEntityProperties(entityID, propertyFlags); + auto name = properties.getName(); auto dimensions = properties.getDimensions(); + const glm::vec3 DEFAULT_DIMENSIONS = glm::vec3(0.1f, 0.1f, 0.1f); if (dimensions != DEFAULT_DIMENSIONS) { // Entity has been auto-resized; adjust dimensions if it seems too big. const float RESCALE_THRESHOLD = 10.0f; // Resize entities larger than this as the FBX is likely in cm or mm. if (dimensions.x > RESCALE_THRESHOLD || dimensions.y > RESCALE_THRESHOLD || dimensions.z > RESCALE_THRESHOLD) { - dimensions *= 0.01f; + auto dimensionsResized = dimensions * 0.01f; EntityItemProperties properties; - properties.setDimensions(dimensions); + properties.setDimensions(dimensionsResized); properties.setVisible(true); properties.setCollisionless(false); properties.setLastEdited(usecTimestampNow()); entityScriptingInterface->editEntity(entityID, properties); - qInfo(interfaceapp) << "Asset auto-resized"; + qInfo(interfaceapp) << "Asset" << name << "auto-resized from" << dimensions << " to " + << dimensionsResized; } item = _addAssetToWorldResizeList.erase(item); // Finished with this entity. @@ -5781,6 +5787,7 @@ void Application::addAssetToWorldCheckModelSize() { properties.setLastEdited(usecTimestampNow()); entityScriptingInterface->editEntity(entityID, properties); + qInfo(interfaceapp) << "Asset" << name << "auto-resize timed out"; item = _addAssetToWorldResizeList.erase(item); // Finished with this entity. } else { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index dc5b6cd8d3..e64c89b1ee 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -533,7 +533,8 @@ void RenderableModelEntityItem::update(const quint64& now) { properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it auto extents = _model->getMeshExtents(); properties.setDimensions(extents.maximum - extents.minimum); - qCDebug(entitiesrenderer) << "Autoresizing:" << (!getName().isEmpty() ? getName() : getModelURL()); + qCDebug(entitiesrenderer) << "Autoresizing" << (!getName().isEmpty() ? getName() : getModelURL()) + << "from mesh extents"; QMetaObject::invokeMethod(DependencyManager::get().data(), "editEntity", Qt::QueuedConnection, Q_ARG(QUuid, getEntityItemID()), From ea7d84ec7d0fcbed37f0b4ae2d6b545951fdb75b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 13 Dec 2016 17:55:44 +1300 Subject: [PATCH 071/101] Increase auto-resize timeout for Clara.io asset download from ATP --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e62a25a53a..6d6946d34e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5777,7 +5777,7 @@ void Application::addAssetToWorldCheckModelSize() { // Increment count of checks done. _addAssetToWorldResizeList[entityID]++; - const int CHECK_MODEL_SIZE_MAX_CHECKS = 10; + const int CHECK_MODEL_SIZE_MAX_CHECKS = 30; if (_addAssetToWorldResizeList[entityID] > CHECK_MODEL_SIZE_MAX_CHECKS) { // Have done enough checks; model was either the default size or something's gone wrong. From c9a00e86d1cff90ab0195cf593c90ee923f739e2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 13 Dec 2016 18:31:24 +1300 Subject: [PATCH 072/101] Fix model being left invisible if not auto-resized --- interface/src/Application.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6d6946d34e..adb0a7869f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5757,19 +5757,20 @@ void Application::addAssetToWorldCheckModelSize() { const glm::vec3 DEFAULT_DIMENSIONS = glm::vec3(0.1f, 0.1f, 0.1f); if (dimensions != DEFAULT_DIMENSIONS) { // Entity has been auto-resized; adjust dimensions if it seems too big. - + EntityItemProperties properties; const float RESCALE_THRESHOLD = 10.0f; // Resize entities larger than this as the FBX is likely in cm or mm. if (dimensions.x > RESCALE_THRESHOLD || dimensions.y > RESCALE_THRESHOLD || dimensions.z > RESCALE_THRESHOLD) { auto dimensionsResized = dimensions * 0.01f; - EntityItemProperties properties; properties.setDimensions(dimensionsResized); - properties.setVisible(true); - properties.setCollisionless(false); - properties.setLastEdited(usecTimestampNow()); - entityScriptingInterface->editEntity(entityID, properties); - qInfo(interfaceapp) << "Asset" << name << "auto-resized from" << dimensions << " to " + qInfo(interfaceapp) << "Asset" << name << "auto-resized from" << dimensions << " to " << dimensionsResized; + } else { + qInfo(interfaceapp) << "Asset" << name << "does not need to be auto-resized"; } + properties.setVisible(true); + properties.setCollisionless(false); + properties.setLastEdited(usecTimestampNow()); + entityScriptingInterface->editEntity(entityID, properties); item = _addAssetToWorldResizeList.erase(item); // Finished with this entity. @@ -5786,8 +5787,8 @@ void Application::addAssetToWorldCheckModelSize() { properties.setCollisionless(false); properties.setLastEdited(usecTimestampNow()); entityScriptingInterface->editEntity(entityID, properties); - qInfo(interfaceapp) << "Asset" << name << "auto-resize timed out"; + item = _addAssetToWorldResizeList.erase(item); // Finished with this entity. } else { From 20623ebddbbc919050c63adc8e4e4fecf0bdfb79 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 13 Dec 2016 22:48:17 +1300 Subject: [PATCH 073/101] Raise and lower keyboard on password fields --- interface/resources/html/raiseAndLowerKeyboard.js | 2 +- scripts/system/html/js/keyboardControl.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/html/raiseAndLowerKeyboard.js b/interface/resources/html/raiseAndLowerKeyboard.js index 0fb5529052..63e016c5d4 100644 --- a/interface/resources/html/raiseAndLowerKeyboard.js +++ b/interface/resources/html/raiseAndLowerKeyboard.js @@ -19,7 +19,7 @@ function shouldRaiseKeyboard() { var nodeName = document.activeElement.nodeName; var nodeType = document.activeElement.type; - if (nodeName === "INPUT" && (nodeType === "text" || nodeType === "number") + if (nodeName === "INPUT" && (nodeType === "text" || nodeType === "number" || nodeType === "password") || document.activeElement.nodeName === "TEXTAREA") { return true; } else { diff --git a/scripts/system/html/js/keyboardControl.js b/scripts/system/html/js/keyboardControl.js index 964f5f5786..f2937bc63a 100644 --- a/scripts/system/html/js/keyboardControl.js +++ b/scripts/system/html/js/keyboardControl.js @@ -56,7 +56,7 @@ function setUpKeyboardControl() { } } - var inputs = document.querySelectorAll("input[type=text], input[type=number], textarea"); + var inputs = document.querySelectorAll("input[type=text], input[type=password], input[type=number], textarea"); for (var i = 0, length = inputs.length; i < length; i++) { inputs[i].addEventListener("focus", raiseKeyboard); inputs[i].addEventListener("blur", lowerKeyboard); From 819539320595e18da6c32fcf2f6669c7cf8aa00c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 13 Dec 2016 22:54:19 +1300 Subject: [PATCH 074/101] Fix Clara marketplace button appearing on top of navigation footer --- scripts/system/html/js/marketplacesInject.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 96fa1c1faa..507a554e5c 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -18,7 +18,7 @@ // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. $("head").append( '' ); @@ -38,12 +39,13 @@ ); // Footer. - var isInitialDirectoryPage = location.href.match(/\/scripts\/system\/html\/marketplaces\.html$/); + var isInitialHiFiPage = location.href === "https://metaverse.highfidelity.com/marketplace?"; $("body").append( '
' + - (isInitialDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + - (!isInitialDirectoryPage ? '' : '') + + (!isInitialHiFiPage ? '' : '') + + (isInitialHiFiPage ? '🛈 See also other marketplaces.' : '') + (!isDirectoryPage ? '' : '') + + (isDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + '
' ); diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index e2e15d83cc..976c0f294f 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -48,7 +48,7 @@
-