From 74ea1548a929f4936684cc1d9ab59adfc553a85a Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 13 Sep 2018 14:24:49 -0700 Subject: [PATCH 01/37] Use chrono::system_clock for usecTimestampNow() on all platforms --- libraries/shared/src/SharedUtil.cpp | 81 ++--------------------------- 1 file changed, 5 insertions(+), 76 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index bb22a1e753..886445824b 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include #include @@ -122,87 +122,16 @@ void setGlobalInstance(const char* propertyName, const QVariant& variant) { } } +// Do we still need this? static qint64 usecTimestampNowAdjust = 0; // in usec void usecTimestampNowForceClockSkew(qint64 clockSkew) { ::usecTimestampNowAdjust = clockSkew; } -static std::atomic TIME_REFERENCE { 0 }; // in usec -static std::once_flag usecTimestampNowIsInitialized; -static QElapsedTimer timestampTimer; - quint64 usecTimestampNow(bool wantDebug) { - std::call_once(usecTimestampNowIsInitialized, [&] { - TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec - timestampTimer.start(); - }); - - quint64 now; - quint64 nsecsElapsed = timestampTimer.nsecsElapsed(); - quint64 usecsElapsed = nsecsElapsed / NSECS_PER_USEC; // nsec to usec - - // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it - // will begin to deviate from real time. We detect that here, and reset if necessary - quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch(); - quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / USECS_PER_MSEC; // usecs to msecs - int possibleSkew = msecsEstimate - msecsCurrentTime; - const int TOLERANCE = 10 * MSECS_PER_SECOND; // up to 10 seconds of skew is tolerated - if (abs(possibleSkew) > TOLERANCE) { - // reset our TIME_REFERENCE and timer - TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec - timestampTimer.restart(); - now = TIME_REFERENCE + ::usecTimestampNowAdjust; - - if (wantDebug) { - qCDebug(shared) << "usecTimestampNow() - resetting QElapsedTimer. "; - qCDebug(shared) << " msecsCurrentTime:" << msecsCurrentTime; - qCDebug(shared) << " msecsEstimate:" << msecsEstimate; - qCDebug(shared) << " possibleSkew:" << possibleSkew; - qCDebug(shared) << " TOLERANCE:" << TOLERANCE; - - qCDebug(shared) << " nsecsElapsed:" << nsecsElapsed; - qCDebug(shared) << " usecsElapsed:" << usecsElapsed; - - QDateTime currentLocalTime = QDateTime::currentDateTime(); - - quint64 msecsNow = now / 1000; // usecs to msecs - QDateTime nowAsString; - nowAsString.setMSecsSinceEpoch(msecsNow); - - qCDebug(shared) << " now:" << now; - qCDebug(shared) << " msecsNow:" << msecsNow; - - qCDebug(shared) << " nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz"); - qCDebug(shared) << " currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz"); - } - } else { - now = TIME_REFERENCE + usecsElapsed + ::usecTimestampNowAdjust; - } - - if (wantDebug) { - QDateTime currentLocalTime = QDateTime::currentDateTime(); - - quint64 msecsNow = now / 1000; // usecs to msecs - QDateTime nowAsString; - nowAsString.setMSecsSinceEpoch(msecsNow); - - quint64 msecsTimeReference = TIME_REFERENCE / 1000; // usecs to msecs - QDateTime timeReferenceAsString; - timeReferenceAsString.setMSecsSinceEpoch(msecsTimeReference); - - qCDebug(shared) << "usecTimestampNow() - details... "; - qCDebug(shared) << " TIME_REFERENCE:" << TIME_REFERENCE; - qCDebug(shared) << " timeReferenceAsString:" << timeReferenceAsString.toString("yyyy-MM-dd hh:mm:ss.zzz"); - qCDebug(shared) << " usecTimestampNowAdjust:" << usecTimestampNowAdjust; - qCDebug(shared) << " nsecsElapsed:" << nsecsElapsed; - qCDebug(shared) << " usecsElapsed:" << usecsElapsed; - qCDebug(shared) << " now:" << now; - qCDebug(shared) << " msecsNow:" << msecsNow; - qCDebug(shared) << " nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz"); - qCDebug(shared) << " currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz"); - } - - return now; + using namespace std::chrono; + static const auto unixEpoch = system_clock::from_time_t(0); + return duration_cast(system_clock::now() - unixEpoch).count() + usecTimestampNowAdjust; } float secTimestampNow() { From 727aea20251af8bbe3c2b77d4ec35b9feda02662 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 13 Sep 2018 18:21:16 -0700 Subject: [PATCH 02/37] Remove comment --- libraries/shared/src/SharedUtil.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 886445824b..012e7aa1f5 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -122,7 +122,6 @@ void setGlobalInstance(const char* propertyName, const QVariant& variant) { } } -// Do we still need this? static qint64 usecTimestampNowAdjust = 0; // in usec void usecTimestampNowForceClockSkew(qint64 clockSkew) { ::usecTimestampNowAdjust = clockSkew; From f2a046da9160edafec7b94d838f7ed19983a46ce Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Thu, 13 Sep 2018 12:15:18 -0700 Subject: [PATCH 03/37] Install marketplace item tester --- .../MarketplaceItemTester.qml | 157 ++++++++++++++++++ interface/src/Application.cpp | 9 +- interface/src/commerce/QmlCommerce.cpp | 1 + scripts/system/commerce/wallet.js | 23 +++ 4 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml new file mode 100644 index 0000000000..80cf82678f --- /dev/null +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -0,0 +1,157 @@ +// +// marketplaceItemTester +// qml/hifi/commerce/marketplaceItemTester +// +// Load items not in the marketplace for testing purposes +// +// Created by Zach Fox on 2018-09-05 +// Copyright 2018 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.Styles 1.4 +import QtQuick.Dialogs 1.0 +import QtQuick.Layouts 1.1 +import Hifi 1.0 as Hifi +import "../../../styles-uit" as HifiStylesUit +import "../../../controls-uit" as HifiControlsUit + + + +Rectangle { + id:root + HifiStylesUit.HifiConstants { id: hifi } + color: hifi.colors.white + ListModel { id: listModel } + ListView { + anchors.fill: parent + anchors.leftMargin: 12 + anchors.bottomMargin: 40 + model: listModel + spacing: 5 + delegate: RowLayout { + anchors.left: parent.left + width: parent.width + spacing: 5 + Text { + text: { + var match = resource.match(/\/([^/]*)$/); + return match ? match[1] : resource; + } + font.pointSize: 12 + Layout.preferredWidth: root.width * .6 + horizontalAlignment: Text.AlignBottom + } + Text { + text: assetType + font.pointSize: 10 + Layout.preferredWidth: root.width * .2 + horizontalAlignment: Text.AlignBottom + } + property var actions: { + "forward": function(resource, assetType){ + if ("application" == assetType) { + Commerce.installApp(resource); + Commerce.openApp(resource); + } + // XXX support other resource types here. + }, + "trash": function(){ + if ("application" == assetType) { + Commerce.uninstallApp(resource); + } + // XXX support other resource types here. + listModel.remove(index); + } + } + Repeater { + model: [ + { "name": "forward", "glyph": hifi.glyphs.forward, "size": 30 }, + { "name": "trash", "glyph": hifi.glyphs.trash, "size": 22} + ] + HifiStylesUit.HiFiGlyphs { + text: modelData.glyph + size: modelData.size + color: hifi.colors.black + horizontalAlignment: Text.AlignHCenter + MouseArea { + anchors.fill: parent + onClicked: { + actions[modelData.name](resource, assetType); + } + } + } + } + } + headerPositioning: ListView.OverlayHeader + header: HifiStylesUit.RalewayRegular { + id: rootHeader + text: "Marketplace Item Tester" + height: 80 + width: paintedWidth + size: 22 + color: hifi.colors.black + anchors.left: parent.left + anchors.leftMargin: 12 + } + footerPositioning: ListView.OverlayFooter + footer: Row { + id: rootActions + spacing: 20 + anchors.horizontalCenter: parent.horizontalCenter + property string currentAction + function assetType(resource) { + return (resource.match(/\.app\.json$/) ? "application" : + resource.match(/\.(?:fbx|fst)$/) ? "avatar" : + resource.match(/\.json\.gz$/) ? "content set" : + resource.match(/\.json$/) ? "entity or wearable" : + "unknown") + } + function onResourceSelected(resource) { + // It is possible that we received the present signal + // from something other than our browserAsync window. + // Alas, there is nothing we can do about that so charge + // ahead as though we are sure the present signal is one + // we expect. + if ("load file" == currentAction) { + print("disconnecting load file"); + Window.browseChanged.disconnect(onResourceSelected); + } else if ("load url" == currentAction) { + print("disconnecting load url"); + Window.promptTextChanged.disconnect(onResourceSelected); + } + if (resource) { + listModel.append( { + "resource": resource.trim(), + "assetType": assetType(resource.trim()) } ); + } + } + property var actions: { + "Load File": function(){ + rootActions.currentAction = "load file"; + Window.browseChanged.connect(onResourceSelected); + Window.browseAsync("Please select a file", "", "Assets (*.app.json *.json *.fbx *.json.gz)"); + }, + "Load URL": function(){ + rootActions.currentAction = "load url"; + Window.promptTextChanged.connect(onResourceSelected); + Window.promptAsync("Please enter a URL", ""); + } + } + Repeater { + model: [ "Load File", "Load URL" ] + HifiControlsUit.Button { + color: hifi.buttons.blue + fontSize: 20 + text: modelData + width: root.width / 3 + height: 40 + onClicked: actions[text]() + } + } + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 826f6a87a9..0415475397 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1691,21 +1691,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return DependencyManager::get()->navigationFocused() ? 1 : 0; }); _applicationStateDevice->setInputVariant(STATE_PLATFORM_WINDOWS, []() -> float { -#if defined(Q_OS_WIN) +#if defined(Q_OS_WIN) return 1; #else return 0; #endif }); _applicationStateDevice->setInputVariant(STATE_PLATFORM_MAC, []() -> float { -#if defined(Q_OS_MAC) +#if defined(Q_OS_MAC) return 1; #else return 0; #endif }); _applicationStateDevice->setInputVariant(STATE_PLATFORM_ANDROID, []() -> float { -#if defined(Q_OS_ANDROID) +#if defined(Q_OS_ANDROID) return 1; #else return 0; @@ -2881,9 +2881,10 @@ void Application::initializeUi() { QUrl{ "hifi/commerce/common/CommerceLightbox.qml" }, QUrl{ "hifi/commerce/common/EmulatedMarketplaceHeader.qml" }, QUrl{ "hifi/commerce/common/FirstUseTutorial.qml" }, - QUrl{ "hifi/commerce/common/SortableListModel.qml" }, QUrl{ "hifi/commerce/common/sendAsset/SendAsset.qml" }, + QUrl{ "hifi/commerce/common/SortableListModel.qml" }, QUrl{ "hifi/commerce/inspectionCertificate/InspectionCertificate.qml" }, + QUrl{ "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"}, QUrl{ "hifi/commerce/purchases/PurchasedItem.qml" }, QUrl{ "hifi/commerce/purchases/Purchases.qml" }, QUrl{ "hifi/commerce/wallet/Help.qml" }, diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 06da18148f..10be228310 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -228,6 +228,7 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) { // Thus, we protect against deleting the .app.json from the user's disk (below) // by skipping that check for the app we just installed. if ((justInstalledAppID != "") && ((justInstalledAppID + ".app.json") == appFileName)) { + installedAppsFromMarketplace += appFileName + ","; continue; } diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5939b36438..12ec7dce6b 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -583,6 +583,27 @@ // // Manage the connection between the button and the window. // + var DEVELOPER_MENU = "Developer"; + var MARKETPLACE_ITEM_TESTER_LABEL = "Marktplace Item Tester"; + var MARKETPLACE_ITEM_TESTER_QML_SOURCE = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"; + function installMarketplaceItemTester() { + if (!Menu.menuExists(DEVELOPER_MENU)) { + Menu.addMenu(DEVELOPER_MENU); + } + + if (!Menu.menuItemExists(DEVELOPER_MENU, MARKETPLACE_ITEM_TESTER_LABEL)) { + Menu.addMenuItem({ menuName: DEVELOPER_MENU, + menuItemName: MARKETPLACE_ITEM_TESTER_LABEL, + isCheckable: false }) + } + + Menu.menuItemEvent.connect(function (menuItem) { + if (menuItem === MARKETPLACE_ITEM_TESTER_LABEL) { + tablet.loadQMLSource(MARKETPLACE_ITEM_TESTER_QML_SOURCE); + } + }); + } + var button; var buttonName = "WALLET"; var tablet = null; @@ -600,7 +621,9 @@ button.clicked.connect(onButtonClicked); tablet.screenChanged.connect(onTabletScreenChanged); } + installMarketplaceItemTester(); } + var isWired = false; var isUpdateOverlaysWired = false; function off() { From 8f5923d5624026d492ef5067dc915ccdfe56d4dc Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 18 Sep 2018 11:23:32 -0700 Subject: [PATCH 04/37] Show installed apps on start of marketplace item tester Also, refactor a bit. --- .../MarketplaceItemTester.qml | 120 ++++++++++++------ 1 file changed, 79 insertions(+), 41 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 80cf82678f..b36deb8b70 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -22,39 +22,61 @@ import "../../../controls-uit" as HifiControlsUit Rectangle { - id:root + id: root + property string installedApps HifiStylesUit.HifiConstants { id: hifi } + ListModel { id: resourceListModel } + color: hifi.colors.white - ListModel { id: listModel } + + function buildResourceObj(resource) { + resource = resource.trim(); + var assetType = (resource.match(/\.app\.json$/) ? "application" : + resource.match(/\.(?:fbx|fst)$/) ? "avatar" : + resource.match(/\.json\.gz$/) ? "content set" : + resource.match(/\.json$/) ? "entity or wearable" : + "unknown"); + return { "resource": resource, "assetType": assetType }; + } + + function installResourceObj(resourceObj) { + if ("application" == resourceObj["assetType"]) { + Commerce.installApp(resourceObj["resource"]); + } + // XXX support other asset types here + } + + function addAllInstalledAppsToList() { + var i, apps = Commerce.getInstalledApps().split(","), len = apps.length; + for(i = 0; i < len - 1; ++i) { + if (i in apps) { + resourceListModel.append(buildResourceObj(apps[i])); + } + } + } + + Component.onCompleted: { + // On startup, list includes all tester-installed assets. + addAllInstalledAppsToList(); + // XXX support other asset types here + } + ListView { anchors.fill: parent anchors.leftMargin: 12 anchors.bottomMargin: 40 - model: listModel + anchors.rightMargin: 12 + model: resourceListModel spacing: 5 + delegate: RowLayout { anchors.left: parent.left width: parent.width spacing: 5 - Text { - text: { - var match = resource.match(/\/([^/]*)$/); - return match ? match[1] : resource; - } - font.pointSize: 12 - Layout.preferredWidth: root.width * .6 - horizontalAlignment: Text.AlignBottom - } - Text { - text: assetType - font.pointSize: 10 - Layout.preferredWidth: root.width * .2 - horizontalAlignment: Text.AlignBottom - } + property var actions: { "forward": function(resource, assetType){ if ("application" == assetType) { - Commerce.installApp(resource); Commerce.openApp(resource); } // XXX support other resource types here. @@ -64,9 +86,27 @@ Rectangle { Commerce.uninstallApp(resource); } // XXX support other resource types here. - listModel.remove(index); + resourceListModel.remove(index); } } + + Text { + text: { + var match = resource.match(/\/([^/]*)$/); + return match ? match[1] : resource; + } + font.pointSize: 12 + Layout.preferredWidth: root.width * .6 + horizontalAlignment: Text.AlignBottom + } + + Text { + text: assetType + font.pointSize: 10 + Layout.preferredWidth: root.width * .2 + horizontalAlignment: Text.AlignBottom + } + Repeater { model: [ { "name": "forward", "glyph": hifi.glyphs.forward, "size": 30 }, @@ -86,6 +126,7 @@ Rectangle { } } } + headerPositioning: ListView.OverlayHeader header: HifiStylesUit.RalewayRegular { id: rootHeader @@ -97,19 +138,27 @@ Rectangle { anchors.left: parent.left anchors.leftMargin: 12 } + footerPositioning: ListView.OverlayFooter footer: Row { id: rootActions spacing: 20 anchors.horizontalCenter: parent.horizontalCenter + property string currentAction - function assetType(resource) { - return (resource.match(/\.app\.json$/) ? "application" : - resource.match(/\.(?:fbx|fst)$/) ? "avatar" : - resource.match(/\.json\.gz$/) ? "content set" : - resource.match(/\.json$/) ? "entity or wearable" : - "unknown") + property var actions: { + "Load File": function(){ + rootActions.currentAction = "load file"; + Window.browseChanged.connect(onResourceSelected); + Window.browseAsync("Please select a file", "", "Assets (*.app.json *.json *.fbx *.json.gz)"); + }, + "Load URL": function(){ + rootActions.currentAction = "load url"; + Window.promptTextChanged.connect(onResourceSelected); + Window.promptAsync("Please enter a URL", ""); + } } + function onResourceSelected(resource) { // It is possible that we received the present signal // from something other than our browserAsync window. @@ -124,23 +173,12 @@ Rectangle { Window.promptTextChanged.disconnect(onResourceSelected); } if (resource) { - listModel.append( { - "resource": resource.trim(), - "assetType": assetType(resource.trim()) } ); - } - } - property var actions: { - "Load File": function(){ - rootActions.currentAction = "load file"; - Window.browseChanged.connect(onResourceSelected); - Window.browseAsync("Please select a file", "", "Assets (*.app.json *.json *.fbx *.json.gz)"); - }, - "Load URL": function(){ - rootActions.currentAction = "load url"; - Window.promptTextChanged.connect(onResourceSelected); - Window.promptAsync("Please enter a URL", ""); + var resourceObj = buildResourceObj(resource); + installResourceObj(resourceObj); + resourceListModel.append(resourceObj); } } + Repeater { model: [ "Load File", "Load URL" ] HifiControlsUit.Button { From 645ad2bb7a3493a73f5aace63a49b8d03b605759 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 18 Sep 2018 12:36:03 -0700 Subject: [PATCH 05/37] Enable marketplace item test for avatar url --- .../marketplaceItemTester/MarketplaceItemTester.qml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index b36deb8b70..fed8480239 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -76,8 +76,12 @@ Rectangle { property var actions: { "forward": function(resource, assetType){ - if ("application" == assetType) { - Commerce.openApp(resource); + switch(assetType) { + case "application": + Commerce.openApp(resource); + break + case "avatar": + MyAvatar.useFullAvatarURL(resource); } // XXX support other resource types here. }, From a592565b7b0ee6c941cb6e6de561d0bbd1bbe160 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 18 Sep 2018 13:25:01 -0700 Subject: [PATCH 06/37] Enable marketplace item test for avatar file --- .../commerce/marketplaceItemTester/MarketplaceItemTester.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index fed8480239..4ef6109752 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -32,7 +32,7 @@ Rectangle { function buildResourceObj(resource) { resource = resource.trim(); var assetType = (resource.match(/\.app\.json$/) ? "application" : - resource.match(/\.(?:fbx|fst)$/) ? "avatar" : + resource.match(/\.fst$/) ? "avatar" : resource.match(/\.json\.gz$/) ? "content set" : resource.match(/\.json$/) ? "entity or wearable" : "unknown"); @@ -154,7 +154,7 @@ Rectangle { "Load File": function(){ rootActions.currentAction = "load file"; Window.browseChanged.connect(onResourceSelected); - Window.browseAsync("Please select a file", "", "Assets (*.app.json *.json *.fbx *.json.gz)"); + Window.browseAsync("Please select a file (*.app.json *.json *.fst *.json.gz)", "", "Assets (*.app.json *.json *.fst *.json.gz)"); }, "Load URL": function(){ rootActions.currentAction = "load url"; From cfa9d185f02dfd764abb7cf2a7473ebaa097f5c0 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 18 Sep 2018 14:02:16 -0700 Subject: [PATCH 07/37] Add full resource paths to list in marketplace item tester --- .../MarketplaceItemTester.qml | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 4ef6109752..8b0ecf3436 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -94,21 +94,32 @@ Rectangle { } } - Text { - text: { - var match = resource.match(/\/([^/]*)$/); - return match ? match[1] : resource; - } - font.pointSize: 12 + Column { Layout.preferredWidth: root.width * .6 - horizontalAlignment: Text.AlignBottom + spacing: 5 + Text { + text: { + var match = resource.match(/\/([^/]*)$/); + return match ? match[1] : resource; + } + font.pointSize: 12 + horizontalAlignment: Text.AlignBottom + } + Text { + text: resource + font.pointSize: 8 + width: root.width * .6 + horizontalAlignment: Text.AlignBottom + wrapMode: Text.WrapAnywhere + } } Text { text: assetType font.pointSize: 10 Layout.preferredWidth: root.width * .2 - horizontalAlignment: Text.AlignBottom + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Test.AlignVCenter } Repeater { From 3a7785d10fcd660876d81d9a0437d1821d756bef Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 18 Sep 2018 14:01:12 -0700 Subject: [PATCH 08/37] fix for deadlock in other avatar entity removal --- interface/src/avatar/AvatarManager.cpp | 40 +++++++++------- interface/src/avatar/AvatarManager.h | 5 +- libraries/avatars/src/AvatarHashMap.cpp | 62 ++++++++++++++++++------- libraries/avatars/src/AvatarHashMap.h | 2 + 4 files changed, 74 insertions(+), 35 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 1faf17ea9a..d92a5c81da 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -456,25 +456,29 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar } void AvatarManager::clearOtherAvatars() { - // Remove other avatars from the world but don't actually remove them from _avatarHash - // each will either be removed on timeout or will re-added to the world on receipt of update. - const render::ScenePointer& scene = qApp->getMain3DScene(); - render::Transaction transaction; - - QReadLocker locker(&_hashLock); - AvatarHash::iterator avatarIterator = _avatarHash.begin(); - while (avatarIterator != _avatarHash.end()) { - auto avatar = std::static_pointer_cast(avatarIterator.value()); - if (avatar != _myAvatar) { - handleRemovedAvatar(avatar); - avatarIterator = _avatarHash.erase(avatarIterator); - } else { - ++avatarIterator; - } - } - assert(scene); - scene->enqueueTransaction(transaction); _myAvatar->clearLookAtTargetAvatar(); + + // setup a vector of removed avatars outside the scope of the hash lock + std::vector removedAvatars; + + { + QWriteLocker locker(&_hashLock); + + auto avatarIterator = _avatarHash.begin(); + while (avatarIterator != _avatarHash.end()) { + auto avatar = std::static_pointer_cast(avatarIterator.value()); + if (avatar != _myAvatar) { + removedAvatars.push_back(avatar); + avatarIterator = _avatarHash.erase(avatarIterator); + } else { + ++avatarIterator; + } + } + } + + for (auto& av : removedAvatars) { + handleRemovedAvatar(av); + } } void AvatarManager::deleteAllAvatars() { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 306ba6f39b..407b3c50de 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -204,7 +204,10 @@ private: void simulateAvatarFades(float deltaTime); AvatarSharedPointer newSharedAvatar() override; - void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; + + // must not be called while holding the hash lock + void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, + KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarsToFade; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index d205a915f8..2ee51cca17 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -66,6 +66,22 @@ void AvatarReplicas::removeReplicas(const QUuid& parentID) { } } +std::vector AvatarReplicas::takeReplicas(const QUuid& parentID) { + std::vector replicas; + + auto it = _replicasMap.find(parentID); + + if (it != _replicasMap.end()) { + // take a copy of the replica shared pointers for this parent + replicas = it->second; + + // erase the replicas for this parent from our map + _replicasMap.erase(it); + } + + return replicas; +} + void AvatarReplicas::processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) { if (_replicasMap.find(parentID) != _replicasMap.end()) { auto &replicas = _replicasMap[parentID]; @@ -386,24 +402,31 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S } void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) { - QWriteLocker locker(&_hashLock); + std::vector removedAvatars; - auto replicaIDs = _replicas.getReplicaIDs(sessionUUID); - _replicas.removeReplicas(sessionUUID); - for (auto id : replicaIDs) { - auto removedReplica = _avatarHash.take(id); - if (removedReplica) { - handleRemovedAvatar(removedReplica, removalReason); + { + QWriteLocker locker(&_hashLock); + + auto replicas = _replicas.takeReplicas(sessionUUID); + + for (auto& replica : replicas) { + auto removedReplica = _avatarHash.take(replica->getID()); + if (removedReplica) { + removedAvatars.push_back(removedReplica); + } + } + + _pendingAvatars.remove(sessionUUID); + auto removedAvatar = _avatarHash.take(sessionUUID); + + if (removedAvatar) { + removedAvatars.push_back(removedAvatar); } } - _pendingAvatars.remove(sessionUUID); - auto removedAvatar = _avatarHash.take(sessionUUID); - - if (removedAvatar) { + for (auto& removedAvatar: removedAvatars) { handleRemovedAvatar(removedAvatar, removalReason); } - } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { @@ -421,11 +444,18 @@ void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& ol } void AvatarHashMap::clearOtherAvatars() { - QWriteLocker locker(&_hashLock); + QList removedAvatars; - for (auto& av : _avatarHash) { - handleRemovedAvatar(av); + { + QWriteLocker locker(&_hashLock); + + // grab a copy of the current avatars so we can call handleRemoveAvatar for them + removedAvatars = _avatarHash.values(); + + _avatarHash.clear(); } - _avatarHash.clear(); + for (auto& av : removedAvatars) { + handleRemovedAvatar(av); + } } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 70d7f8c04d..724dd1deac 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -49,6 +49,7 @@ public: void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); void processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); void removeReplicas(const QUuid& parentID); + std::vector takeReplicas(const QUuid& parentID); void processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData); void processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, @@ -180,6 +181,7 @@ protected: virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID) const; // uses a QReadLocker on the hashLock virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason); + // must not be called while holding the hash lock virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason); AvatarHash _avatarHash; From 795580f4e1076e2ba9800d54f82e621f2cc030b7 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 19 Sep 2018 09:55:15 -0700 Subject: [PATCH 09/37] adding 404 redirect toggle with interstitial --- interface/src/ConnectionMonitor.cpp | 28 ++++++++++++++++++---- libraries/networking/src/AddressManager.h | 21 ++++++---------- libraries/networking/src/DomainHandler.cpp | 20 +++++++++++----- 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp index 3c85cfb339..5eacc5259f 100644 --- a/interface/src/ConnectionMonitor.cpp +++ b/interface/src/ConnectionMonitor.cpp @@ -18,11 +18,15 @@ #include #include #include +#include // Because the connection monitor is created at startup, the time we wait on initial load // should be longer to allow the application to initialize. static const int ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS = 10000; static const int REDIRECT_AFTER_DISCONNECTED_FOR_X_MS = 5000; +static const int ON_INITIAL_LOAD_DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 10000; +static const int DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 5000; +Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; void ConnectionMonitor::init() { // Connect to domain disconnected message @@ -37,20 +41,36 @@ void ConnectionMonitor::init() { _timer.setSingleShot(true); if (!domainHandler.isConnected()) { - _timer.start(ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); + if (enableInterstitialMode.get()) { + _timer.start(ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); + } else { + _timer.start(ON_INITIAL_LOAD_DISPLAY_AFTER_DISCONNECTED_FOR_X_MS); + } } connect(&_timer, &QTimer::timeout, this, [this]() { - qDebug() << "ConnectionMonitor: Redirecting to 404 error domain"; // set in a timeout error - emit setRedirectErrorState(REDIRECT_HIFI_ADDRESS, 5); + if (enableInterstitialMode.get()) { + qDebug() << "ConnectionMonitor: Redirecting to 404 error domain"; + emit setRedirectErrorState(REDIRECT_HIFI_ADDRESS, 5); + } else { + qDebug() << "ConnectionMonitor: Showing connection failure window"; + DependencyManager::get()->setDomainConnectionFailureVisibility(true); + } }); } void ConnectionMonitor::startTimer() { - _timer.start(REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); + if (enableInterstitialMode.get()) { + _timer.start(REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); + } else { + _timer.start(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS); + } } void ConnectionMonitor::stopTimer() { _timer.stop(); + if (!enableInterstitialMode.get()) { + DependencyManager::get()->setDomainConnectionFailureVisibility(false); + } } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index c7cdf8f4ea..17041a5fd7 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -140,8 +140,7 @@ public: * * @typedef {number} location.LookupTrigger */ - enum LookupTrigger - { + enum LookupTrigger { UserInput, Back, Forward, @@ -207,9 +206,8 @@ public slots: // functions and signals that should be exposed are moved to a scripting interface class. // // we currently expect this to be called from NodeList once handleLookupString has been called with a path - bool goToViewpointForPath(const QString& viewpointString, const QString& pathString) { - return handleViewpoint(viewpointString, false, DomainPathResponse, false, pathString); - } + bool goToViewpointForPath(const QString& viewpointString, const QString& pathString) + { return handleViewpoint(viewpointString, false, DomainPathResponse, false, pathString); } /**jsdoc * Go back to the previous location in your navigation history, if there is one. @@ -231,8 +229,7 @@ public slots: * location history is correctly maintained. */ void goToLocalSandbox(QString path = "", LookupTrigger trigger = LookupTrigger::StartupFromSettings) { - handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger); - } + handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger); } /**jsdoc * Go to the default "welcome" metaverse address. @@ -364,8 +361,7 @@ signals: * location.locationChangeRequired.connect(onLocationChangeRequired); */ void locationChangeRequired(const glm::vec3& newPosition, - bool hasOrientationChange, - const glm::quat& newOrientation, + bool hasOrientationChange, const glm::quat& newOrientation, bool shouldFaceLocation); /**jsdoc @@ -448,11 +444,8 @@ private: bool handleNetworkAddress(const QString& lookupString, LookupTrigger trigger, bool& hostChanged); void handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly = false); - bool handleViewpoint(const QString& viewpointString, - bool shouldFace, - LookupTrigger trigger, - bool definitelyPathOnly = false, - const QString& pathString = QString()); + bool handleViewpoint(const QString& viewpointString, bool shouldFace, LookupTrigger trigger, + bool definitelyPathOnly = false, const QString& pathString = QString()); bool handleUsername(const QString& lookupString); bool handleDomainID(const QString& host); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index f34a93de96..d02cb75e8b 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -29,6 +30,8 @@ #include "UserActivityLogger.h" #include "NetworkLogging.h" +Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; + DomainHandler::DomainHandler(QObject* parent) : QObject(parent), _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), @@ -485,14 +488,19 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer(); From 01594d7a52a07c4a7a2e5443fbdcca6b8328a18e Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 19 Sep 2018 10:17:14 -0700 Subject: [PATCH 10/37] fixing settings check in domain handler --- libraries/networking/src/DomainHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index d02cb75e8b..d2d576d8b7 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -30,8 +30,6 @@ #include "UserActivityLogger.h" #include "NetworkLogging.h" -Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; - DomainHandler::DomainHandler(QObject* parent) : QObject(parent), _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), @@ -488,6 +486,8 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer enableInterstitialMode{ "enableInterstitialMode", false }; + if (enableInterstitialMode.get()) { if (reasonCode == ConnectionRefusedReason::ProtocolMismatch || reasonCode == ConnectionRefusedReason::NotAuthorized) { // ingest the error - this is a "hard" connection refusal. From a43799fffa0199352221535f8237a0f1c18fc3dc Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 19 Sep 2018 10:40:49 -0700 Subject: [PATCH 11/37] moving settings check within slotted functions --- interface/src/ConnectionMonitor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp index 5eacc5259f..8f0c12634c 100644 --- a/interface/src/ConnectionMonitor.cpp +++ b/interface/src/ConnectionMonitor.cpp @@ -26,7 +26,6 @@ static const int ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS = 10000; static const int REDIRECT_AFTER_DISCONNECTED_FOR_X_MS = 5000; static const int ON_INITIAL_LOAD_DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 10000; static const int DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 5000; -Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; void ConnectionMonitor::init() { // Connect to domain disconnected message @@ -41,6 +40,7 @@ void ConnectionMonitor::init() { _timer.setSingleShot(true); if (!domainHandler.isConnected()) { + Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; if (enableInterstitialMode.get()) { _timer.start(ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); } else { @@ -50,6 +50,7 @@ void ConnectionMonitor::init() { connect(&_timer, &QTimer::timeout, this, [this]() { // set in a timeout error + Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; if (enableInterstitialMode.get()) { qDebug() << "ConnectionMonitor: Redirecting to 404 error domain"; emit setRedirectErrorState(REDIRECT_HIFI_ADDRESS, 5); @@ -61,6 +62,7 @@ void ConnectionMonitor::init() { } void ConnectionMonitor::startTimer() { + Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; if (enableInterstitialMode.get()) { _timer.start(REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); } else { @@ -70,6 +72,7 @@ void ConnectionMonitor::startTimer() { void ConnectionMonitor::stopTimer() { _timer.stop(); + Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; if (!enableInterstitialMode.get()) { DependencyManager::get()->setDomainConnectionFailureVisibility(false); } From 7e796f7e7f268a8ab3fd49c4b1f3ef90e41c2447 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 19 Sep 2018 14:26:30 -0700 Subject: [PATCH 12/37] committing requested changes --- interface/src/ConnectionMonitor.cpp | 13 ++++--------- interface/src/ConnectionMonitor.h | 5 ++++- libraries/networking/src/DomainHandler.cpp | 7 ++----- libraries/networking/src/DomainHandler.h | 3 +++ 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp index 8f0c12634c..ca90d039f4 100644 --- a/interface/src/ConnectionMonitor.cpp +++ b/interface/src/ConnectionMonitor.cpp @@ -18,7 +18,6 @@ #include #include #include -#include // Because the connection monitor is created at startup, the time we wait on initial load // should be longer to allow the application to initialize. @@ -40,8 +39,7 @@ void ConnectionMonitor::init() { _timer.setSingleShot(true); if (!domainHandler.isConnected()) { - Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; - if (enableInterstitialMode.get()) { + if (_enableInterstitialMode.get()) { _timer.start(ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); } else { _timer.start(ON_INITIAL_LOAD_DISPLAY_AFTER_DISCONNECTED_FOR_X_MS); @@ -50,8 +48,7 @@ void ConnectionMonitor::init() { connect(&_timer, &QTimer::timeout, this, [this]() { // set in a timeout error - Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; - if (enableInterstitialMode.get()) { + if (_enableInterstitialMode.get()) { qDebug() << "ConnectionMonitor: Redirecting to 404 error domain"; emit setRedirectErrorState(REDIRECT_HIFI_ADDRESS, 5); } else { @@ -62,8 +59,7 @@ void ConnectionMonitor::init() { } void ConnectionMonitor::startTimer() { - Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; - if (enableInterstitialMode.get()) { + if (_enableInterstitialMode.get()) { _timer.start(REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); } else { _timer.start(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS); @@ -72,8 +68,7 @@ void ConnectionMonitor::startTimer() { void ConnectionMonitor::stopTimer() { _timer.stop(); - Setting::Handle enableInterstitialMode{ "enableInterstitialMode", false }; - if (!enableInterstitialMode.get()) { + if (!_enableInterstitialMode.get()) { DependencyManager::get()->setDomainConnectionFailureVisibility(false); } } diff --git a/interface/src/ConnectionMonitor.h b/interface/src/ConnectionMonitor.h index 5e75e2618b..f0589a3b8c 100644 --- a/interface/src/ConnectionMonitor.h +++ b/interface/src/ConnectionMonitor.h @@ -15,6 +15,8 @@ #include #include +#include + class QUrl; class QString; @@ -32,6 +34,7 @@ private slots: private: QTimer _timer; + Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false }; }; -#endif // hifi_ConnectionMonitor_h \ No newline at end of file +#endif // hifi_ConnectionMonitor_h diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index d2d576d8b7..b2f118c5c8 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include @@ -486,9 +485,8 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer enableInterstitialMode{ "enableInterstitialMode", false }; - if (enableInterstitialMode.get()) { + if (_enableInterstitialMode.get()) { if (reasonCode == ConnectionRefusedReason::ProtocolMismatch || reasonCode == ConnectionRefusedReason::NotAuthorized) { // ingest the error - this is a "hard" connection refusal. setRedirectErrorState(_errorDomainURL, (int)reasonCode); @@ -496,8 +494,7 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer #include +#include + #include "HifiSockAddr.h" #include "NetworkPeer.h" #include "NLPacket.h" @@ -221,6 +223,7 @@ private: NetworkPeer _icePeer; bool _isConnected { false }; bool _isInErrorState { false }; + Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false }; QJsonObject _settingsObject; QString _pendingPath; QTimer _settingsTimer; From 5bc7d944f961fb34e3d5b2ce1e35dd2e952ad5ea Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Wed, 19 Sep 2018 15:17:13 -0700 Subject: [PATCH 13/37] Remove whitespace --- .../hifi/commerce/purchases/PurchasedItem.qml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 032d9b0199..eeb9ac3c54 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -59,7 +59,7 @@ Item { Connections { target: Commerce; - + onContentSetChanged: { if (contentSetHref === root.itemHref) { showConfirmation = true; @@ -135,7 +135,7 @@ Item { anchors.topMargin: 8; width: 30; height: width; - + HiFiGlyphs { id: closeContextMenuGlyph; text: hifi.glyphs.close; @@ -376,7 +376,7 @@ Item { } } } - + transform: Rotation { id: rotation; origin.x: flipable.width/2; @@ -509,7 +509,7 @@ Item { } verticalAlignment: Text.AlignTop; } - + HiFiGlyphs { id: statusIcon; text: { @@ -588,7 +588,7 @@ Item { border.width: 1; border.color: "#E2334D"; } - + HiFiGlyphs { id: contextMenuGlyph; text: hifi.glyphs.verticalEllipsis; @@ -615,7 +615,7 @@ Item { } } } - + Rectangle { id: rezzedNotifContainer; z: 998; @@ -663,13 +663,13 @@ Item { Tablet.playSound(TabletEnums.ButtonHover); } } - + onFocusChanged: { if (focus) { Tablet.playSound(TabletEnums.ButtonHover); } } - + onClicked: { Tablet.playSound(TabletEnums.ButtonClick); if (root.itemType === "contentSet") { @@ -775,7 +775,7 @@ Item { // Style color: hifi.colors.redAccent; horizontalAlignment: Text.AlignRight; - + MouseArea { anchors.fill: parent; hoverEnabled: true; From 66bf45c3ef1819b98ec880c4559592f94660a571 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Wed, 19 Sep 2018 15:18:18 -0700 Subject: [PATCH 14/37] Update location only if certificate id --- interface/src/commerce/QmlCommerce.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 10be228310..7c5df0f3e3 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -199,12 +199,18 @@ void QmlCommerce::transferAssetToUsername(const QString& username, } void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) { - auto ledger = DependencyManager::get(); - ledger->updateLocation(certificateID, DependencyManager::get()->getPlaceName(), true); + if (!certificateID.isEmpty()) { + auto ledger = DependencyManager::get(); + ledger->updateLocation( + certificateID, + DependencyManager::get()->getPlaceName(), + true); + } qApp->replaceDomainContent(itemHref); - QJsonObject messageProperties = { { "status", "SuccessfulRequestToReplaceContent" }, { "content_set_url", itemHref } }; + QJsonObject messageProperties = { + { "status", "SuccessfulRequestToReplaceContent" }, + { "content_set_url", itemHref } }; UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties); - emit contentSetChanged(itemHref); } From 38fd9523ee25081005753486dbd45d067080f358 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Wed, 19 Sep 2018 15:57:29 -0700 Subject: [PATCH 15/37] Attempt to load content set This does not result in the user seeing the content set. --- .../MarketplaceItemTester.qml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 8b0ecf3436..69e1a6122c 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -55,6 +55,11 @@ Rectangle { } } + function isHttp(str) { + var httpPattern = /^http/i; + return httpPattern.test(str); + } + Component.onCompleted: { // On startup, list includes all tester-installed assets. addAllInstalledAppsToList(); @@ -79,9 +84,15 @@ Rectangle { switch(assetType) { case "application": Commerce.openApp(resource); - break + break; case "avatar": MyAvatar.useFullAvatarURL(resource); + break; + case "content set": + resource = isHttp(resource) ? resource : "file:///" + resource; + Commerce.replaceContentSet(resource, ""); + urlHandler.handleUrl("hifi://localhost/0,0,0"); + break; } // XXX support other resource types here. }, @@ -119,7 +130,7 @@ Rectangle { font.pointSize: 10 Layout.preferredWidth: root.width * .2 horizontalAlignment: Text.AlignHCenter - verticalAlignment: Test.AlignVCenter + verticalAlignment: Text.AlignVCenter } Repeater { From 5445d6f6704ab702947a4da45ddf2ec528e96bc5 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Wed, 19 Sep 2018 16:06:43 -0700 Subject: [PATCH 16/37] Switch to localhost before replacing content --- .../commerce/marketplaceItemTester/MarketplaceItemTester.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 69e1a6122c..23c3bc976f 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -90,8 +90,8 @@ Rectangle { break; case "content set": resource = isHttp(resource) ? resource : "file:///" + resource; - Commerce.replaceContentSet(resource, ""); urlHandler.handleUrl("hifi://localhost/0,0,0"); + Commerce.replaceContentSet(resource, ""); break; } // XXX support other resource types here. From afa8e44e66e359475ec07b68f1cc0f72121f0096 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Thu, 20 Sep 2018 10:53:32 -0700 Subject: [PATCH 17/37] Refactor --- .../marketplaceItemTester/MarketplaceItemTester.qml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 23c3bc976f..d582e9aa82 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -55,9 +55,9 @@ Rectangle { } } - function isHttp(str) { + function toUrl(resource) { var httpPattern = /^http/i; - return httpPattern.test(str); + return httpPattern.test(str) ? resource : "file:///" + resource; } Component.onCompleted: { @@ -89,9 +89,10 @@ Rectangle { MyAvatar.useFullAvatarURL(resource); break; case "content set": - resource = isHttp(resource) ? resource : "file:///" + resource; urlHandler.handleUrl("hifi://localhost/0,0,0"); - Commerce.replaceContentSet(resource, ""); + Commerce.replaceContentSet(toUrl(resource), ""); + break; + case "entity": break; } // XXX support other resource types here. From ae4352f450ba93ecf448b1f74434ecc7614800c9 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Thu, 20 Sep 2018 13:55:22 -0700 Subject: [PATCH 18/37] Rez entity in marketplace item tester --- .../MarketplaceItemTester.qml | 16 +++++- scripts/system/marketplaces/marketplaces.js | 51 ++++++++++++++----- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index d582e9aa82..9927c072db 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -23,7 +23,10 @@ import "../../../controls-uit" as HifiControlsUit Rectangle { id: root + property string installedApps + signal sendToScript(var message) + HifiStylesUit.HifiConstants { id: hifi } ListModel { id: resourceListModel } @@ -57,7 +60,11 @@ Rectangle { function toUrl(resource) { var httpPattern = /^http/i; - return httpPattern.test(str) ? resource : "file:///" + resource; + return httpPattern.test(resource) ? resource : "file:///" + resource; + } + + function rezEntity(itemHref, itemType) { + } Component.onCompleted: { @@ -92,7 +99,12 @@ Rectangle { urlHandler.handleUrl("hifi://localhost/0,0,0"); Commerce.replaceContentSet(toUrl(resource), ""); break; - case "entity": + case "entity or wearable": + print("going to rez " + toUrl(resource)); + sendToScript({ + method: 'tester_rezClicked', + itemHref: toUrl(resource), + itemType: "entity"}); break; } // XXX support other resource types here. diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 7b4f05193f..01c21044d2 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -21,20 +21,20 @@ var selectionDisplay = null; // for gridTool.js to ignore Script.include("/~/system/libraries/gridTool.js"); Script.include("/~/system/libraries/connectionUtils.js"); - var METAVERSE_SERVER_URL = Account.metaverseServerURL; + // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; + var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; + var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml"; + var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "commerce/inspectionCertificate/InspectionCertificate.qml"; + var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"; + var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. - var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); - var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); - var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml"; - var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; - var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "commerce/inspectionCertificate/InspectionCertificate.qml"; + var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); + var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); + var METAVERSE_SERVER_URL = Account.metaverseServerURL; var REZZING_SOUND = SoundCache.getSound(Script.resolvePath("../assets/sounds/rezzing.wav")); - var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; - // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; - // Event bridge messages. var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; var CLARA_IO_STATUS = "CLARA.IO STATUS"; @@ -786,7 +786,7 @@ var selectionDisplay = null; // for gridTool.js to ignore } sendAssetRecipient = null; } - + var savedDisablePreviewOptionLocked = false; var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview");; function maybeEnableHMDPreview() { @@ -855,6 +855,8 @@ var selectionDisplay = null; // for gridTool.js to ignore break; case 'checkout_rezClicked': case 'purchases_rezClicked': + case 'tester_rezClicked': + print("marketplace.js going to rez"); rezEntity(message.itemHref, message.itemType); break; case 'header_marketplaceImageClicked': @@ -1032,19 +1034,40 @@ var selectionDisplay = null; // for gridTool.js to ignore // value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML. var onWalletScreen = false; var onCommerceScreen = false; + var onMarketplaceItemTesterScreen = false; function onTabletScreenChanged(type, url) { onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; var onWalletScreenNow = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1; - var onCommerceScreenNow = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH - || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1); + var onCommerceScreenNow = type === "QML" && ( + url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH) !== -1 || + url === MARKETPLACE_PURCHASES_QML_PATH || + url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1); + var onMarketplaceItemTesterScreenNow = ( + url.indexOf(MARKETPLACE_ITEM_TESTER_QML_PATH) !== -1 || + url === MARKETPLACE_ITEM_TESTER_QML_PATH); - if ((!onWalletScreenNow && onWalletScreen) || (!onCommerceScreenNow && onCommerceScreen)) { // exiting wallet or commerce screen + if ((!onWalletScreenNow && onWalletScreen) || + (!onCommerceScreenNow && onCommerceScreen) || + (!onMarketplaceItemTesterScreenNow && onMarketplaceScreen) + ) { + // exiting wallet, commerce, or marketplace item tester screen maybeEnableHMDPreview(); } onCommerceScreen = onCommerceScreenNow; onWalletScreen = onWalletScreenNow; - wireEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen); + onMarketplaceItemTesterScreen = onMarketplaceItemTesterScreenNow; + + print("wire event bridge " + (onMarketplaceScreen || + onCommerceScreen || + onWalletScreen || + onMarketplaceItemTesterScreen)); + + wireEventBridge( + onMarketplaceScreen || + onCommerceScreen || + onWalletScreen || + onMarketplaceItemTesterScreen); if (url === MARKETPLACE_PURCHASES_QML_PATH) { sendToQml({ From c1cc031bd0c7ddcadb5215bb50ec264f81d8411c Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Thu, 20 Sep 2018 14:50:27 -0700 Subject: [PATCH 19/37] Remove cruft and refactor --- .../MarketplaceItemTester.qml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 9927c072db..37d2f2c170 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -45,8 +45,9 @@ Rectangle { function installResourceObj(resourceObj) { if ("application" == resourceObj["assetType"]) { Commerce.installApp(resourceObj["resource"]); + } else { + print("Cannot install resource object type " + resourceObj["assetType"]); } - // XXX support other asset types here } function addAllInstalledAppsToList() { @@ -63,14 +64,16 @@ Rectangle { return httpPattern.test(resource) ? resource : "file:///" + resource; } - function rezEntity(itemHref, itemType) { - + function rezEntity(resource, entityType) { + sendToScript({ + method: 'tester_rezClicked', + itemHref: toUrl(resource), + itemType: entityType}); } Component.onCompleted: { // On startup, list includes all tester-installed assets. addAllInstalledAppsToList(); - // XXX support other asset types here } ListView { @@ -99,21 +102,18 @@ Rectangle { urlHandler.handleUrl("hifi://localhost/0,0,0"); Commerce.replaceContentSet(toUrl(resource), ""); break; - case "entity or wearable": - print("going to rez " + toUrl(resource)); - sendToScript({ - method: 'tester_rezClicked', - itemHref: toUrl(resource), - itemType: "entity"}); + case "entity": + case "wearable": + rezEntity(resource, assetType); break; + default: + print("Marketplace item tester unsupported assetType " + assetType); } - // XXX support other resource types here. }, "trash": function(){ if ("application" == assetType) { Commerce.uninstallApp(resource); } - // XXX support other resource types here. resourceListModel.remove(index); } } From f9c25f2f32220e4f22969b9d65e8750f82774499 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Thu, 20 Sep 2018 16:36:06 -0700 Subject: [PATCH 20/37] Allow user to select asset type in marketplace item tester --- .../MarketplaceItemTester.qml | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 37d2f2c170..559b6cb29c 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -12,6 +12,7 @@ // import QtQuick 2.5 +import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.0 import QtQuick.Layouts 1.1 @@ -83,6 +84,7 @@ Rectangle { anchors.rightMargin: 12 model: resourceListModel spacing: 5 + interactive: false delegate: RowLayout { anchors.left: parent.left @@ -138,12 +140,25 @@ Rectangle { } } - Text { - text: assetType - font.pointSize: 10 + ComboBox { + id: comboBox + Layout.preferredWidth: root.width * .2 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter + + model: [ + "application", + "avatar", + "content set", + "entity", + "wearable", + "unknown" + ] + + currentIndex: ("entity or wearable" == assetType) ? model.indexOf("unknown") : model.indexOf(assetType) + + Component.onCompleted: { + onActivated.connect(function() { assetType = currentText; }); + } } Repeater { @@ -159,7 +174,7 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - actions[modelData.name](resource, assetType); + actions[modelData.name](resource, comboBox.currentText); } } } From d4450ac7801b043cf885c77a61a1c6db5ce21a2a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 21 Sep 2018 15:59:00 -0700 Subject: [PATCH 21/37] fix equip and keeping new functionallity --- .../controllers/controllerModules/highlightNearbyEntities.js | 2 +- scripts/system/controllers/controllerModules/inEditMode.js | 2 +- scripts/system/controllers/controllerModules/inVREditMode.js | 2 +- .../controllers/controllerModules/nearActionGrabEntity.js | 2 +- .../controllers/controllerModules/nearGrabHyperLinkEntity.js | 2 +- .../controllers/controllerModules/nearParentGrabEntity.js | 2 +- scripts/system/controllers/controllerModules/nearTrigger.js | 2 +- .../controllers/controllerModules/webSurfaceLaserInput.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js index 3a33082f64..bc09ebee7a 100644 --- a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js +++ b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js @@ -37,7 +37,7 @@ this.highlightedEntities = []; this.parameters = dispatcherUtils.makeDispatcherModuleParameters( - 120, + 480, this.hand === dispatcherUtils.RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 2bdd89f141..d590545532 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -29,7 +29,7 @@ Script.include("/~/system/libraries/utils.js"); this.reticleMaxY; this.parameters = makeDispatcherModuleParameters( - 200, + 160, this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], [], 100, diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index 02863cf935..7b78d5e1c4 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -21,7 +21,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.disableModules = false; var NO_HAND_LASER = -1; // Invalid hand parameter so that default laser is not displayed. this.parameters = makeDispatcherModuleParameters( - 240, // Not too high otherwise the tablet laser doesn't work. + 200, // Not too high otherwise the tablet laser doesn't work. this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 27c1b458b8..a8de76aebd 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -26,7 +26,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.hapticTargetID = null; this.parameters = makeDispatcherModuleParameters( - 140, + 500, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); diff --git a/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js b/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js index 366fcd3032..962ae89bb9 100644 --- a/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js +++ b/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js @@ -21,7 +21,7 @@ this.hyperlink = ""; this.parameters = makeDispatcherModuleParameters( - 125, + 485, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index f805dbf60e..cc88371441 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -57,7 +57,7 @@ Script.include("/~/system/libraries/controllers.js"); this.cloneAllowed = true; this.parameters = makeDispatcherModuleParameters( - 140, + 500, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index f1126dedc3..6a9cd9fbcd 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -29,7 +29,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.startSent = false; this.parameters = makeDispatcherModuleParameters( - 120, + 480, this.hand === RIGHT_HAND ? ["rightHandTrigger", "rightHand"] : ["leftHandTrigger", "leftHand"], [], 100); diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index 4e36355621..2412e2fa1c 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -121,7 +121,7 @@ Script.include("/~/system/libraries/controllers.js"); controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE; var allowThisModule = !otherModuleRunning || isTriggerPressed; - if (allowThisModule && this.isPointingAtTriggerable(controllerData, isTriggerPressed, false)) { + if ((allowThisModule && this.isPointingAtTriggerable(controllerData, isTriggerPressed, false)) && !this.grabModuleWantsNearbyOverlay(controllerData)) { this.updateAllwaysOn(); if (isTriggerPressed) { this.dominantHandOverride = true; // Override dominant hand. From b31837168d15d4e5090b41ff03eef443ca34c97f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 24 Sep 2018 11:27:20 -0700 Subject: [PATCH 22/37] fix bad lock, optimize some operations, clarify comment --- interface/src/avatar/AvatarManager.cpp | 4 +++- interface/src/avatar/AvatarManager.h | 4 +++- libraries/avatars/src/AvatarHashMap.cpp | 2 +- libraries/avatars/src/AvatarHashMap.h | 3 +-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index d92a5c81da..0fdd246d7b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -464,6 +464,8 @@ void AvatarManager::clearOtherAvatars() { { QWriteLocker locker(&_hashLock); + removedAvatars.reserve(_avatarHash.size()); + auto avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { auto avatar = std::static_pointer_cast(avatarIterator.value()); @@ -484,7 +486,7 @@ void AvatarManager::clearOtherAvatars() { void AvatarManager::deleteAllAvatars() { assert(_avatarsToChangeInPhysics.empty()); - QReadLocker locker(&_hashLock); + QWriteLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { auto avatar = std::static_pointer_cast(avatarIterator.value()); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 407b3c50de..9c4287728d 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -205,7 +205,9 @@ private: AvatarSharedPointer newSharedAvatar() override; - // must not be called while holding the hash lock + // called only from the AvatarHashMap thread - cannot be called while this thread holds the + // hash lock, since handleRemovedAvatar needs a write lock on the entity tree and the entity tree + // frequently grabs a read lock on the hash to get a given avatar by ID void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 2ee51cca17..01557e307e 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -73,7 +73,7 @@ std::vector AvatarReplicas::takeReplicas(const QUuid& paren if (it != _replicasMap.end()) { // take a copy of the replica shared pointers for this parent - replicas = it->second; + replicas.swap(it->second); // erase the replicas for this parent from our map _replicasMap.erase(it); diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 724dd1deac..c2cb448e52 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -180,8 +180,7 @@ protected: bool& isNew); virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID) const; // uses a QReadLocker on the hashLock virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason); - - // must not be called while holding the hash lock + virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason); AvatarHash _avatarHash; From 69d529936299331669c7913f5a4ca3f13b646be3 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 24 Sep 2018 15:22:46 -0700 Subject: [PATCH 23/37] patching fix for previous commits --- interface/src/Application.cpp | 13 +++--- interface/src/Application.h | 2 +- interface/src/ConnectionMonitor.cpp | 22 +++------- interface/src/ConnectionMonitor.h | 5 +-- .../scripting/WindowScriptingInterface.cpp | 8 ++++ .../src/scripting/WindowScriptingInterface.h | 4 ++ libraries/networking/src/DomainHandler.cpp | 43 ++++++++++++------- libraries/networking/src/DomainHandler.h | 8 +++- 8 files changed, 61 insertions(+), 44 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 46cebc1661..a9aa9077b0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3498,13 +3498,14 @@ bool Application::isServerlessMode() const { } void Application::setIsInterstitialMode(bool interstitialMode) { - Settings settings; - bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool(); - if (_interstitialMode != interstitialMode && enableInterstitial) { - _interstitialMode = interstitialMode; + bool enableInterstitial = DependencyManager::get()->getDomainHandler().getInterstitialModeEnabled(); + if (enableInterstitial) { + if (_interstitialMode != interstitialMode) { + _interstitialMode = interstitialMode; - DependencyManager::get()->setAudioPaused(_interstitialMode); - DependencyManager::get()->setMyAvatarDataPacketsPaused(_interstitialMode); + DependencyManager::get()->setAudioPaused(_interstitialMode); + DependencyManager::get()->setMyAvatarDataPacketsPaused(_interstitialMode); + } } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 3bebc60480..eedbdb7622 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -432,7 +432,7 @@ public slots: void setIsServerlessMode(bool serverlessDomain); void loadServerlessDomain(QUrl domainURL, bool errorDomain = false); - void setIsInterstitialMode(bool interstialMode); + void setIsInterstitialMode(bool interstitialMode); void updateVerboseLogging(); diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp index ca90d039f4..e86061b090 100644 --- a/interface/src/ConnectionMonitor.cpp +++ b/interface/src/ConnectionMonitor.cpp @@ -23,8 +23,6 @@ // should be longer to allow the application to initialize. static const int ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS = 10000; static const int REDIRECT_AFTER_DISCONNECTED_FOR_X_MS = 5000; -static const int ON_INITIAL_LOAD_DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 10000; -static const int DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 5000; void ConnectionMonitor::init() { // Connect to domain disconnected message @@ -39,18 +37,15 @@ void ConnectionMonitor::init() { _timer.setSingleShot(true); if (!domainHandler.isConnected()) { - if (_enableInterstitialMode.get()) { - _timer.start(ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); - } else { - _timer.start(ON_INITIAL_LOAD_DISPLAY_AFTER_DISCONNECTED_FOR_X_MS); - } + _timer.start(ON_INITIAL_LOAD_REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); } connect(&_timer, &QTimer::timeout, this, [this]() { // set in a timeout error - if (_enableInterstitialMode.get()) { + bool enableInterstitial = DependencyManager::get()->getDomainHandler().getInterstitialModeEnabled(); + if (enableInterstitial) { qDebug() << "ConnectionMonitor: Redirecting to 404 error domain"; - emit setRedirectErrorState(REDIRECT_HIFI_ADDRESS, 5); + emit setRedirectErrorState(REDIRECT_HIFI_ADDRESS, "", 5); } else { qDebug() << "ConnectionMonitor: Showing connection failure window"; DependencyManager::get()->setDomainConnectionFailureVisibility(true); @@ -59,16 +54,13 @@ void ConnectionMonitor::init() { } void ConnectionMonitor::startTimer() { - if (_enableInterstitialMode.get()) { - _timer.start(REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); - } else { - _timer.start(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS); - } + _timer.start(REDIRECT_AFTER_DISCONNECTED_FOR_X_MS); } void ConnectionMonitor::stopTimer() { _timer.stop(); - if (!_enableInterstitialMode.get()) { + bool enableInterstitial = DependencyManager::get()->getDomainHandler().getInterstitialModeEnabled(); + if (!enableInterstitial) { DependencyManager::get()->setDomainConnectionFailureVisibility(false); } } diff --git a/interface/src/ConnectionMonitor.h b/interface/src/ConnectionMonitor.h index f0589a3b8c..2fda6ef7cd 100644 --- a/interface/src/ConnectionMonitor.h +++ b/interface/src/ConnectionMonitor.h @@ -15,8 +15,6 @@ #include #include -#include - class QUrl; class QString; @@ -26,7 +24,7 @@ public: void init(); signals: - void setRedirectErrorState(QUrl errorURL, int reasonCode); + void setRedirectErrorState(QUrl errorURL, QString reasonMessage = "", int reasonCode = -1, const QString& extraInfo = ""); private slots: void startTimer(); @@ -34,7 +32,6 @@ private slots: private: QTimer _timer; - Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false }; }; #endif // hifi_ConnectionMonitor_h diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index e3ae65aee1..d4eb37e0aa 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -180,6 +180,14 @@ void WindowScriptingInterface::setPreviousBrowseAssetLocation(const QString& loc Setting::Handle(LAST_BROWSE_ASSETS_LOCATION_SETTING).set(location); } +bool WindowScriptingInterface::getInterstitialModeEnabled() const { + return DependencyManager::get()->getDomainHandler().getInterstitialModeEnabled(); +} + +void WindowScriptingInterface::setInterstitialModeEnabled(bool enableInterstitialMode) { + DependencyManager::get()->getDomainHandler().setInterstitialModeEnabled(enableInterstitialMode); +} + bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) { auto offscreenUi = DependencyManager::get(); return offscreenUi->isPointOnDesktopWindow(point); diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 3827406729..ddd7159f23 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -49,6 +49,7 @@ class WindowScriptingInterface : public QObject, public Dependency { Q_PROPERTY(int innerHeight READ getInnerHeight) Q_PROPERTY(int x READ getX) Q_PROPERTY(int y READ getY) + Q_PROPERTY(bool interstitialModeEnabled READ getInterstitialModeEnabled WRITE setInterstitialModeEnabled) public: WindowScriptingInterface(); @@ -758,6 +759,9 @@ private: QString getPreviousBrowseAssetLocation() const; void setPreviousBrowseAssetLocation(const QString& location); + bool getInterstitialModeEnabled() const; + void setInterstitialModeEnabled(bool enableInterstitialMode); + void ensureReticleVisible() const; int createMessageBox(QString title, QString text, int buttons, int defaultButton); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index b2f118c5c8..df34a1fb59 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -15,6 +15,10 @@ #include +#include + +#include + #include #include @@ -134,6 +138,18 @@ void DomainHandler::hardReset() { _pendingPath.clear(); } +bool DomainHandler::getInterstitialModeEnabled() const { + return _interstitialModeSettingLock.resultWithReadLock([&] { + return _enableInterstitialMode.get(); + }); +} + +void DomainHandler::setInterstitialModeEnabled(bool enableInterstitialMode) { + _interstitialModeSettingLock.withWriteLock([&] { + _enableInterstitialMode.set(enableInterstitialMode); + }); +} + void DomainHandler::setErrorDomainURL(const QUrl& url) { _errorDomainURL = url; return; @@ -340,11 +356,15 @@ void DomainHandler::loadedErrorDomain(std::map namedPaths) { DependencyManager::get()->goToViewpointForPath(viewpoint, QString()); } -void DomainHandler::setRedirectErrorState(QUrl errorUrl, int reasonCode) { - _errorDomainURL = errorUrl; +void DomainHandler::setRedirectErrorState(QUrl errorUrl, QString reasonMessage, int reasonCode, const QString& extraInfo) { _lastDomainConnectionError = reasonCode; - _isInErrorState = true; - emit redirectToErrorDomainURL(_errorDomainURL); + if (getInterstitialModeEnabled()) { + _errorDomainURL = errorUrl; + _isInErrorState = true; + emit redirectToErrorDomainURL(_errorDomainURL); + } else { + emit domainConnectionRefused(reasonMessage, reasonCode, extraInfo); + } } void DomainHandler::requestDomainSettings() { @@ -486,18 +506,9 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 5fa920a554..e9ec20ba2e 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "HifiSockAddr.h" @@ -85,6 +86,8 @@ public: bool isConnected() const { return _isConnected; } void setIsConnected(bool isConnected); bool isServerless() const { return _domainURL.scheme() != URL_SCHEME_HIFI; } + bool getInterstitialModeEnabled() const; + void setInterstitialModeEnabled(bool enableInterstitialMode); void connectedToServerless(std::map namedPaths); @@ -173,7 +176,7 @@ public slots: void processDomainServerConnectionDeniedPacket(QSharedPointer message); // sets domain handler in error state. - void setRedirectErrorState(QUrl errorUrl, int reasonCode); + void setRedirectErrorState(QUrl errorUrl, QString reasonMessage = "", int reason = -1, const QString& extraInfo = ""); bool isInErrorState() { return _isInErrorState; } @@ -223,10 +226,11 @@ private: NetworkPeer _icePeer; bool _isConnected { false }; bool _isInErrorState { false }; - Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false }; QJsonObject _settingsObject; QString _pendingPath; QTimer _settingsTimer; + mutable ReadWriteLockable _interstitialModeSettingLock; + Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false }; QSet _domainConnectionRefusals; bool _hasCheckedForAccessToken { false }; From 697f556a60211c48797f4bc5f685589e0a0ec229 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Mon, 24 Sep 2018 16:30:49 -0700 Subject: [PATCH 24/37] Persist list of marketplace items in test The list of resources between runs of the marketplace item tester now persist between runs of the tester. --- .../MarketplaceItemTester.qml | 54 +++++++++++++----- .../marketplaceItemTester/spinner.gif | Bin 0 -> 46135 bytes scripts/system/marketplaces/marketplaces.js | 52 ++++++++++++++++- 3 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 559b6cb29c..f2dbcc3f80 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -29,10 +29,35 @@ Rectangle { signal sendToScript(var message) HifiStylesUit.HifiConstants { id: hifi } - ListModel { id: resourceListModel } + ListModel { + id: resourceListModel + property var nextId: 0 + } color: hifi.colors.white + AnimatedImage { + id: spinner; + source: "spinner.gif" + width: 74; + height: width; + anchors.verticalCenter: parent.verticalCenter; + anchors.horizontalCenter: parent.horizontalCenter; + } + + function fromScript(message) { + switch (message.method) { + case "newResourceObjectInTest": + var resourceObject = message.resourceObject; + resourceListModel.append(resourceObject); + spinner.visible = false; + break; + case "marketplaceTestBackendIsAlive": + spinner.visible = false; + break; + } + } + function buildResourceObj(resource) { resource = resource.trim(); var assetType = (resource.match(/\.app\.json$/) ? "application" : @@ -40,14 +65,14 @@ Rectangle { resource.match(/\.json\.gz$/) ? "content set" : resource.match(/\.json$/) ? "entity or wearable" : "unknown"); - return { "resource": resource, "assetType": assetType }; + return { "id": resourceListModel.nextId++, + "resource": resource, + "assetType": assetType }; } function installResourceObj(resourceObj) { if ("application" == resourceObj["assetType"]) { Commerce.installApp(resourceObj["resource"]); - } else { - print("Cannot install resource object type " + resourceObj["assetType"]); } } @@ -72,11 +97,6 @@ Rectangle { itemType: entityType}); } - Component.onCompleted: { - // On startup, list includes all tester-installed assets. - addAllInstalledAppsToList(); - } - ListView { anchors.fill: parent anchors.leftMargin: 12 @@ -157,7 +177,13 @@ Rectangle { currentIndex: ("entity or wearable" == assetType) ? model.indexOf("unknown") : model.indexOf(assetType) Component.onCompleted: { - onActivated.connect(function() { assetType = currentText; }); + onCurrentIndexChanged.connect(function() { + assetType = model[currentIndex]; + sendToScript({ + method: 'tester_updateResourceObjectAssetType', + objectId: resourceListModel.get(index)["id"], + assetType: assetType }); + }); } } @@ -220,17 +246,17 @@ Rectangle { // ahead as though we are sure the present signal is one // we expect. if ("load file" == currentAction) { - print("disconnecting load file"); Window.browseChanged.disconnect(onResourceSelected); } else if ("load url" == currentAction) { - print("disconnecting load url"); Window.promptTextChanged.disconnect(onResourceSelected); } if (resource) { var resourceObj = buildResourceObj(resource); installResourceObj(resourceObj); - resourceListModel.append(resourceObj); - } + sendToScript({ + method: 'tester_newResourceObject', + resourceObject: resourceObj }); + } } Repeater { diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif b/interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..00f75ae62fa59b28a7813ca00c4185390e3e51da GIT binary patch literal 46135 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 V-}`%i@9+J+zxVh4-rv9A{u}xRWtji~ literal 0 HcmV?d00001 diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 01c21044d2..a066143d45 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -800,6 +800,18 @@ var selectionDisplay = null; // for gridTool.js to ignore }, 150); } + function signalNewResourceObjectInTest(resourceObject) { + sendToQml({ + method: "newResourceObjectInTest", + resourceObject: resourceObject }); + } + + var resourceObjectsInTest = []; + function storeResourceObjectInTest(resourceObject) { + resourceObjectsInTest.push(resourceObject); + signalNewResourceObjectInTest(resourceObject); + } + // Function Name: fromQml() // // Description: @@ -856,9 +868,22 @@ var selectionDisplay = null; // for gridTool.js to ignore case 'checkout_rezClicked': case 'purchases_rezClicked': case 'tester_rezClicked': - print("marketplace.js going to rez"); rezEntity(message.itemHref, message.itemType); break; + case 'tester_newResourceObject': + storeResourceObjectInTest(message.resourceObject); + break; + case 'tester_updateResourceObjectAssetType': + var objectId = message.objectId; + for (var i = 0, size = resourceObjectsInTest.length; i < size; ++i) { + if (i in resourceObjectsInTest && + objectId === resourceObjectsInTest[i]["id"] + ) { + resourceObjectsInTest[i]["assetType"] = message.assetType; + break; + } + } + break; case 'header_marketplaceImageClicked': case 'purchases_backClicked': tablet.gotoWebScreen(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL); @@ -1027,6 +1052,22 @@ var selectionDisplay = null; // for gridTool.js to ignore } } + function pushResourceObjectsInTest() { + var isQmlSignaled = false; + for (var i = 0, size = resourceObjectsInTest.length; i < size; ++i) { + if (i in resourceObjectsInTest) { + signalNewResourceObjectInTest(resourceObjectsInTest[i]); + isQmlSignaled = true; + } + } + // Be sure that the QML has heard from us, at least so that it + // can indicate to the user that all of the resoruce objects in + // test have been transmitted to it. + if (!isQmlSignaled) { + sendToQml({ method: "marketplaceTestBackendIsAlive" }); + } + } + // Function Name: onTabletScreenChanged() // // Description: @@ -1070,6 +1111,9 @@ var selectionDisplay = null; // for gridTool.js to ignore onMarketplaceItemTesterScreen); if (url === MARKETPLACE_PURCHASES_QML_PATH) { + // FIXME: There is a race condition here. The event bridge + // may not be up yet. Suggest Script.setTimeout(..., 750) to + // help avoid the condition. sendToQml({ method: 'updatePurchases', referrerURL: referrerURL, @@ -1103,6 +1147,12 @@ var selectionDisplay = null; // for gridTool.js to ignore method: 'inspectionCertificate_resetCert' }); } + + if (onMarketplaceItemTesterScreen) { + // Why? The QML event bridge, wired above, needs time to + // come up. + Script.setTimeout(pushResourceObjectsInTest, 750); + } } // From 7f189e4d10f87f7f6ae4bdbc259683928356a41b Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 24 Sep 2018 17:15:56 -0700 Subject: [PATCH 25/37] Make visualPickResult used by Pointers a private copy --- libraries/pointers/src/Pointer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/pointers/src/Pointer.cpp b/libraries/pointers/src/Pointer.cpp index 031baece5f..bdf1250a8d 100644 --- a/libraries/pointers/src/Pointer.cpp +++ b/libraries/pointers/src/Pointer.cpp @@ -68,7 +68,7 @@ void Pointer::update(unsigned int pointerID) { // This only needs to be a read lock because update won't change any of the properties that can be modified from scripts withReadLock([&] { auto pickResult = getPrevPickResult(); - auto visualPickResult = getVisualPickResult(pickResult); + auto visualPickResult = getVisualPickResult(std::make_shared(*pickResult.get())); updateVisuals(visualPickResult); generatePointerEvents(pointerID, visualPickResult); }); From 5dbebd4aae4652fa0109d3da9e11f93cf85151a1 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 24 Sep 2018 17:36:24 -0700 Subject: [PATCH 26/37] Add comment explaining why visualPickResult is a copy --- libraries/pointers/src/Pointer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/pointers/src/Pointer.cpp b/libraries/pointers/src/Pointer.cpp index bdf1250a8d..852a83c192 100644 --- a/libraries/pointers/src/Pointer.cpp +++ b/libraries/pointers/src/Pointer.cpp @@ -68,6 +68,7 @@ void Pointer::update(unsigned int pointerID) { // This only needs to be a read lock because update won't change any of the properties that can be modified from scripts withReadLock([&] { auto pickResult = getPrevPickResult(); + // Pointer needs its own PickResult object so it doesn't modify the cached pick result auto visualPickResult = getVisualPickResult(std::make_shared(*pickResult.get())); updateVisuals(visualPickResult); generatePointerEvents(pointerID, visualPickResult); From 0a43c17ce0d97458e57b972a92107806d5837be6 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Mon, 24 Sep 2018 21:15:08 -0700 Subject: [PATCH 27/37] Use entity-specific glyphs in marketplace item tester --- .../MarketplaceItemTester.qml | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index f2dbcc3f80..40e4f55f2c 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -188,13 +188,22 @@ Rectangle { } Repeater { - model: [ - { "name": "forward", "glyph": hifi.glyphs.forward, "size": 30 }, - { "name": "trash", "glyph": hifi.glyphs.trash, "size": 22} - ] + model: [ "forward", "trash" ] + HifiStylesUit.HiFiGlyphs { - text: modelData.glyph - size: modelData.size + property var glyphs: { + "application": hifi.glyphs.install, + "avatar": hifi.glyphs.avatar, + "content set": hifi.glyphs.globe, + "entity": hifi.glyphs.wand, + "trash": hifi.glyphs.trash, + "unknown": hifi.glyphs.circleSlash, + "wearable": hifi.glyphs.hat, + } + text: (("trash" == modelData) ? + glyphs.trash : + glyphs[comboBox.model[comboBox.currentIndex]]) + size: ("trash" == modelData) ? 22 : 30 color: hifi.colors.black horizontalAlignment: Text.AlignHCenter MouseArea { From ca1da9c830d8c3bab0a5ea65dda5cc4c3d1c3f32 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Mon, 24 Sep 2018 23:07:44 -0700 Subject: [PATCH 28/37] Implement delete-from-list functionality in marketplace item tester --- .../MarketplaceItemTester.qml | 8 +++++-- scripts/system/marketplaces/marketplaces.js | 24 +++++++------------ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 0a852780b4..c962c84c31 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -136,6 +136,9 @@ Rectangle { if ("application" == assetType) { Commerce.uninstallApp(resource); } + sendToScript({ + method: "tester_deleteResourceObject", + objectId: resourceListModel.get(index).id}); resourceListModel.remove(index); } } @@ -174,13 +177,14 @@ Rectangle { "unknown" ] - currentIndex: ("entity or wearable" == assetType) ? model.indexOf("unknown") : model.indexOf(assetType) + currentIndex: (("entity or wearable" == assetType) ? + model.indexOf("unknown") : model.indexOf(assetType)) Component.onCompleted: { onCurrentIndexChanged.connect(function() { assetType = model[currentIndex]; sendToScript({ - method: 'tester_updateResourceObjectAssetType', + method: "tester_updateResourceObjectAssetType", objectId: resourceListModel.get(index)["id"], assetType: assetType }); }); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index c279e76b16..aba4ba5f7d 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -770,18 +770,13 @@ function maybeEnableHMDPreview() { }, UI_FADE_TIMEOUT_MS); } +var resourceObjectsInTest = []; function signalNewResourceObjectInTest(resourceObject) { ui.tablet.sendToQml({ method: "newResourceObjectInTest", resourceObject: resourceObject }); } -var resourceObjectsInTest = []; -function storeResourceObjectInTest(resourceObject) { - resourceObjectsInTest.push(resourceObject); - signalNewResourceObjectInTest(resourceObject); -} - var onQmlMessageReceived = function onQmlMessageReceived(message) { if (message.messageSrc === "HTML") { return; @@ -835,18 +830,15 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { rezEntity(message.itemHref, message.itemType); break; case 'tester_newResourceObject': - storeResourceObjectInTest(message.resourceObject); + var resourceObject = message.resourceObject; + resourceObjectsInTest[resourceObject.id] = resourceObject; + signalNewResourceObjectInTest(resourceObject); break; case 'tester_updateResourceObjectAssetType': - var objectId = message.objectId; - for (var i = 0, size = resourceObjectsInTest.length; i < size; ++i) { - if (i in resourceObjectsInTest && - objectId === resourceObjectsInTest[i]["id"] - ) { - resourceObjectsInTest[i]["assetType"] = message.assetType; - break; - } - } + resourceObjectsInTest[message.objectId].assetType = message.assetType; + break; + case 'tester_deleteResourceObject': + delete resourceObjectsInTest[message.objectId]; break; case 'header_marketplaceImageClicked': case 'purchases_backClicked': From ff83713365b4b4fa5c69f733506d656f2745a826 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Mon, 24 Sep 2018 23:57:48 -0700 Subject: [PATCH 29/37] Use persistent object id counter --- .../MarketplaceItemTester.qml | 17 +++++++-------- scripts/system/marketplaces/marketplaces.js | 21 ++++++++----------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index c962c84c31..a0ccf91baa 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -26,13 +26,11 @@ Rectangle { id: root property string installedApps + property var nextResourceObjectId: 0 signal sendToScript(var message) HifiStylesUit.HifiConstants { id: hifi } - ListModel { - id: resourceListModel - property var nextId: 0 - } + ListModel { id: resourceListModel } color: hifi.colors.white @@ -52,7 +50,8 @@ Rectangle { resourceListModel.append(resourceObject); spinner.visible = false; break; - case "marketplaceTestBackendIsAlive": + case "nextObjectIdInTest": + nextResourceObjectId = message.id; spinner.visible = false; break; } @@ -65,14 +64,14 @@ Rectangle { resource.match(/\.json\.gz$/) ? "content set" : resource.match(/\.json$/) ? "entity or wearable" : "unknown"); - return { "id": resourceListModel.nextId++, + return { "id": nextResourceObjectId++, "resource": resource, "assetType": assetType }; } function installResourceObj(resourceObj) { - if ("application" == resourceObj["assetType"]) { - Commerce.installApp(resourceObj["resource"]); + if ("application" == resourceObj.assetType) { + Commerce.installApp(resourceObj.resource); } } @@ -269,7 +268,7 @@ Rectangle { sendToScript({ method: 'tester_newResourceObject', resourceObject: resourceObj }); - } + } } Repeater { diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index aba4ba5f7d..2e0c05be43 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -980,19 +980,16 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { }; function pushResourceObjectsInTest() { - var isQmlSignaled = false; - for (var i = 0, size = resourceObjectsInTest.length; i < size; ++i) { - if (i in resourceObjectsInTest) { - signalNewResourceObjectInTest(resourceObjectsInTest[i]); - isQmlSignaled = true; - } - } - // Be sure that the QML has heard from us, at least so that it - // can indicate to the user that all of the resoruce objects in - // test have been transmitted to it. - if (!isQmlSignaled) { - ui.tablet.sendToQml({ method: "marketplaceTestBackendIsAlive" }); + var maxObjectId = -1; + for (var objectId in resourceObjectsInTest) { + signalNewResourceObjectInTest(resourceObjectsInTest[objectId]); + maxObjectId = (maxObjectId < objectId) ? parseInt(objectId) : maxObjectId; } + // N.B. Thinking about removing the following sendToQml? Be sure + // that the marketplace item tester QML has heard from us, at least + // so that it can indicate to the user that all of the resoruce + // objects in test have been transmitted to it. + ui.tablet.sendToQml({ method: "nextObjectIdInTest", id: maxObjectId + 1 }); } // Function Name: onTabletScreenChanged() From eca31e7a994a5a0c7841e82eda987f99f972ba5c Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 25 Sep 2018 09:20:57 -0700 Subject: [PATCH 30/37] Fix instantiating abstract class when creating visual pick result for pointers --- interface/src/raypick/LaserPointer.cpp | 5 +++++ interface/src/raypick/LaserPointer.h | 2 ++ interface/src/raypick/ParabolaPointer.cpp | 5 +++++ interface/src/raypick/ParabolaPointer.h | 2 ++ interface/src/raypick/StylusPointer.cpp | 5 +++++ interface/src/raypick/StylusPointer.h | 1 + libraries/pointers/src/Pointer.cpp | 2 +- libraries/pointers/src/Pointer.h | 1 + 8 files changed, 22 insertions(+), 1 deletion(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 5fbe3a90b5..64faf5f9bf 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -35,6 +35,11 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& } } +PickResultPointer LaserPointer::getPickResultCopy(const PickResultPointer& pickResult) const { + auto rayPickResult = std::static_pointer_cast(pickResult); + return std::make_shared(*rayPickResult.get()); +} + QVariantMap LaserPointer::toVariantMap() const { QVariantMap qVariantMap; diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index c0ac3259d9..b391f60f85 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -47,6 +47,8 @@ public: static std::shared_ptr buildRenderState(const QVariantMap& propMap); protected: + PickResultPointer getPickResultCopy(const PickResultPointer& pickResult) const override; + void editRenderStatePath(const std::string& state, const QVariant& pathProps) override; glm::vec3 getPickOrigin(const PickResultPointer& pickResult) const override; diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 888b3ddbe8..e7f54d2e97 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -30,6 +30,11 @@ ParabolaPointer::ParabolaPointer(const QVariant& rayProps, const RenderStateMap& { } +PickResultPointer ParabolaPointer::getPickResultCopy(const PickResultPointer& pickResult) const { + auto stylusPickResult = std::static_pointer_cast(pickResult); + return std::make_shared(*stylusPickResult.get()); +} + void ParabolaPointer::editRenderStatePath(const std::string& state, const QVariant& pathProps) { auto renderState = std::static_pointer_cast(_renderStates[state]); if (renderState) { diff --git a/interface/src/raypick/ParabolaPointer.h b/interface/src/raypick/ParabolaPointer.h index 526abe3b0d..8fb864c07b 100644 --- a/interface/src/raypick/ParabolaPointer.h +++ b/interface/src/raypick/ParabolaPointer.h @@ -102,6 +102,8 @@ public: static std::shared_ptr buildRenderState(const QVariantMap& propMap); protected: + virtual PickResultPointer getPickResultCopy(const PickResultPointer& pickResult) const override; + void editRenderStatePath(const std::string& state, const QVariant& pathProps) override; glm::vec3 getPickOrigin(const PickResultPointer& pickResult) const override; diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 06e3e52d21..7f05a706a4 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -147,6 +147,11 @@ bool StylusPointer::shouldTrigger(const PickResultPointer& pickResult) { return false; } +PickResultPointer StylusPointer::getPickResultCopy(const PickResultPointer& pickResult) const { + auto stylusPickResult = std::static_pointer_cast(pickResult); + return std::make_shared(*stylusPickResult.get()); +} + Pointer::PickedObject StylusPointer::getHoveredObject(const PickResultPointer& pickResult) { auto stylusPickResult = std::static_pointer_cast(pickResult); if (!stylusPickResult) { diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index 4095acb529..ff60fd78e5 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -42,6 +42,7 @@ protected: Buttons getPressedButtons(const PickResultPointer& pickResult) override; bool shouldHover(const PickResultPointer& pickResult) override; bool shouldTrigger(const PickResultPointer& pickResult) override; + virtual PickResultPointer getPickResultCopy(const PickResultPointer& pickResult) const override; PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override; diff --git a/libraries/pointers/src/Pointer.cpp b/libraries/pointers/src/Pointer.cpp index 852a83c192..26460cbdd7 100644 --- a/libraries/pointers/src/Pointer.cpp +++ b/libraries/pointers/src/Pointer.cpp @@ -69,7 +69,7 @@ void Pointer::update(unsigned int pointerID) { withReadLock([&] { auto pickResult = getPrevPickResult(); // Pointer needs its own PickResult object so it doesn't modify the cached pick result - auto visualPickResult = getVisualPickResult(std::make_shared(*pickResult.get())); + auto visualPickResult = getVisualPickResult(getPickResultCopy(pickResult)); updateVisuals(visualPickResult); generatePointerEvents(pointerID, visualPickResult); }); diff --git a/libraries/pointers/src/Pointer.h b/libraries/pointers/src/Pointer.h index 4264a60079..173163374f 100644 --- a/libraries/pointers/src/Pointer.h +++ b/libraries/pointers/src/Pointer.h @@ -91,6 +91,7 @@ protected: virtual bool shouldHover(const PickResultPointer& pickResult) { return true; } virtual bool shouldTrigger(const PickResultPointer& pickResult) { return true; } + virtual PickResultPointer getPickResultCopy(const PickResultPointer& pickResult) const = 0; virtual PickResultPointer getVisualPickResult(const PickResultPointer& pickResult) { return pickResult; }; static const float POINTER_MOVE_DELAY; From 660bdf36d01b83996aacc93e9997eac41b70f463 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 25 Sep 2018 09:56:11 -0700 Subject: [PATCH 31/37] Check if pick result is null when building visual pick result --- interface/src/raypick/LaserPointer.cpp | 3 +++ interface/src/raypick/ParabolaPointer.cpp | 3 +++ interface/src/raypick/StylusPointer.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 64faf5f9bf..79bca0449f 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -36,6 +36,9 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& } PickResultPointer LaserPointer::getPickResultCopy(const PickResultPointer& pickResult) const { + if (!pickResult) { + std::make_shared(); + } auto rayPickResult = std::static_pointer_cast(pickResult); return std::make_shared(*rayPickResult.get()); } diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 4614b81cbb..ec4222d5f6 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -31,6 +31,9 @@ ParabolaPointer::ParabolaPointer(const QVariant& rayProps, const RenderStateMap& } PickResultPointer ParabolaPointer::getPickResultCopy(const PickResultPointer& pickResult) const { + if (!pickResult) { + std::make_shared(); + } auto stylusPickResult = std::static_pointer_cast(pickResult); return std::make_shared(*stylusPickResult.get()); } diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 7f05a706a4..2742c68d1d 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -148,6 +148,9 @@ bool StylusPointer::shouldTrigger(const PickResultPointer& pickResult) { } PickResultPointer StylusPointer::getPickResultCopy(const PickResultPointer& pickResult) const { + if (!pickResult) { + std::make_shared(); + } auto stylusPickResult = std::static_pointer_cast(pickResult); return std::make_shared(*stylusPickResult.get()); } From 6e83e96d1cff5c000fe30f22e052ec4815749c60 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 25 Sep 2018 12:05:47 -0700 Subject: [PATCH 32/37] Check type in Pointer::getPickResultCopy --- interface/src/raypick/LaserPointer.cpp | 4 ++-- interface/src/raypick/ParabolaPointer.cpp | 6 +++--- interface/src/raypick/StylusPointer.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 79bca0449f..577978cc88 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -36,10 +36,10 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& } PickResultPointer LaserPointer::getPickResultCopy(const PickResultPointer& pickResult) const { - if (!pickResult) { + auto rayPickResult = std::dynamic_pointer_cast(pickResult); + if (!rayPickResult) { std::make_shared(); } - auto rayPickResult = std::static_pointer_cast(pickResult); return std::make_shared(*rayPickResult.get()); } diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index ec4222d5f6..92b82fff7f 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -31,11 +31,11 @@ ParabolaPointer::ParabolaPointer(const QVariant& rayProps, const RenderStateMap& } PickResultPointer ParabolaPointer::getPickResultCopy(const PickResultPointer& pickResult) const { - if (!pickResult) { + auto parabolaPickResult = std::dynamic_pointer_cast(pickResult); + if (!parabolaPickResult) { std::make_shared(); } - auto stylusPickResult = std::static_pointer_cast(pickResult); - return std::make_shared(*stylusPickResult.get()); + return std::make_shared(*parabolaPickResult.get()); } void ParabolaPointer::editRenderStatePath(const std::string& state, const QVariant& pathProps) { diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 2742c68d1d..0b44b2705d 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -148,10 +148,10 @@ bool StylusPointer::shouldTrigger(const PickResultPointer& pickResult) { } PickResultPointer StylusPointer::getPickResultCopy(const PickResultPointer& pickResult) const { - if (!pickResult) { + auto stylusPickResult = std::dynamic_pointer_cast(pickResult); + if (!stylusPickResult) { std::make_shared(); } - auto stylusPickResult = std::static_pointer_cast(pickResult); return std::make_shared(*stylusPickResult.get()); } From 55ab0a394b81706e7d5776cf253258e688d014a8 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 25 Sep 2018 12:21:00 -0700 Subject: [PATCH 33/37] Return std::make_shared --- interface/src/raypick/LaserPointer.cpp | 2 +- interface/src/raypick/ParabolaPointer.cpp | 2 +- interface/src/raypick/StylusPointer.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 577978cc88..3c66923b4e 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -38,7 +38,7 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& PickResultPointer LaserPointer::getPickResultCopy(const PickResultPointer& pickResult) const { auto rayPickResult = std::dynamic_pointer_cast(pickResult); if (!rayPickResult) { - std::make_shared(); + return std::make_shared(); } return std::make_shared(*rayPickResult.get()); } diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 92b82fff7f..33fa8738d9 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -33,7 +33,7 @@ ParabolaPointer::ParabolaPointer(const QVariant& rayProps, const RenderStateMap& PickResultPointer ParabolaPointer::getPickResultCopy(const PickResultPointer& pickResult) const { auto parabolaPickResult = std::dynamic_pointer_cast(pickResult); if (!parabolaPickResult) { - std::make_shared(); + return std::make_shared(); } return std::make_shared(*parabolaPickResult.get()); } diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 0b44b2705d..b648e125bf 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -150,7 +150,7 @@ bool StylusPointer::shouldTrigger(const PickResultPointer& pickResult) { PickResultPointer StylusPointer::getPickResultCopy(const PickResultPointer& pickResult) const { auto stylusPickResult = std::dynamic_pointer_cast(pickResult); if (!stylusPickResult) { - std::make_shared(); + return std::make_shared(); } return std::make_shared(*stylusPickResult.get()); } From 4e7b03883f2731160c036ae59fbb0c98daa22612 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 25 Sep 2018 15:13:26 -0700 Subject: [PATCH 34/37] Use triple equals --- .../marketplaceItemTester/MarketplaceItemTester.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index a0ccf91baa..8dfb277197 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -132,7 +132,7 @@ Rectangle { } }, "trash": function(){ - if ("application" == assetType) { + if ("application" === assetType) { Commerce.uninstallApp(resource); } sendToScript({ @@ -176,7 +176,7 @@ Rectangle { "unknown" ] - currentIndex: (("entity or wearable" == assetType) ? + currentIndex: (("entity or wearable" === assetType) ? model.indexOf("unknown") : model.indexOf(assetType)) Component.onCompleted: { @@ -203,10 +203,10 @@ Rectangle { "unknown": hifi.glyphs.circleSlash, "wearable": hifi.glyphs.hat, } - text: (("trash" == modelData) ? + text: (("trash" === modelData) ? glyphs.trash : glyphs[comboBox.model[comboBox.currentIndex]]) - size: ("trash" == modelData) ? 22 : 30 + size: ("trash" === modelData) ? 22 : 30 color: hifi.colors.black horizontalAlignment: Text.AlignHCenter MouseArea { @@ -257,7 +257,7 @@ Rectangle { // Alas, there is nothing we can do about that so charge // ahead as though we are sure the present signal is one // we expect. - if ("load file" == currentAction) { + if ("load file" === currentAction) { Window.browseChanged.disconnect(onResourceSelected); } else if ("load url" == currentAction) { Window.promptTextChanged.disconnect(onResourceSelected); From 276125937c3ab37b8eec7905fd0678f302c50d60 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 25 Sep 2018 15:18:12 -0700 Subject: [PATCH 35/37] Eliminate double equals --- .../marketplaceItemTester/MarketplaceItemTester.qml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 8dfb277197..8f391f24c0 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -70,7 +70,7 @@ Rectangle { } function installResourceObj(resourceObj) { - if ("application" == resourceObj.assetType) { + if ("application" === resourceObj.assetType) { Commerce.installApp(resourceObj.resource); } } @@ -257,10 +257,13 @@ Rectangle { // Alas, there is nothing we can do about that so charge // ahead as though we are sure the present signal is one // we expect. - if ("load file" === currentAction) { - Window.browseChanged.disconnect(onResourceSelected); - } else if ("load url" == currentAction) { - Window.promptTextChanged.disconnect(onResourceSelected); + switch(currentAction) { + case "load file": + Window.browseChanged.disconnect(onResourceSelected); + break + case "load url": + Window.promptTextChanged.disconnect(onResourceSelected); + break; } if (resource) { var resourceObj = buildResourceObj(resource); From e6249e690e2a5ff951c02b76f2fb8ee102258fcb Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 25 Sep 2018 15:52:26 -0700 Subject: [PATCH 36/37] Remove duplicate line --- scripts/system/marketplaces/marketplaces.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 2e0c05be43..cc5ff99673 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -23,7 +23,6 @@ Script.include("/~/system/libraries/connectionUtils.js"); var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml"; var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml"; var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"; -var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"; var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); From 28ec98be9e274d40edb8b83e0da89f6e1a92f10e Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 25 Sep 2018 19:24:46 -0700 Subject: [PATCH 37/37] Uninstall marketplace item tester on wallet shutdown --- scripts/system/commerce/wallet.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 2c64b35b00..b12191b00c 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -521,6 +521,14 @@ function installMarketplaceItemTester() { }); } +function uninstallMarketplaceItemTester() { + if (Menu.menuExists(DEVELOPER_MENU) && + Menu.menuItemExists(DEVELOPER_MENU, MARKETPLACE_ITEM_TESTER_LABEL) + ) { + Menu.removeMenuItem(DEVELOPER_MENU, MARKETPLACE_ITEM_TESTER_LABEL); + } +} + var BUTTON_NAME = "WALLET"; var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; var ui; @@ -555,6 +563,7 @@ function off() { function shutdown() { GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); deleteSendMoneyParticleEffect(); + uninstallMarketplaceItemTester(); off(); }