diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 7f088d8183..8af4eec934 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AudioMixer.h" + #include #include @@ -36,8 +38,6 @@ #include "AvatarAudioStream.h" #include "InjectedAudioStream.h" -#include "AudioMixer.h" - static const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.5f; // attenuation = -6dB * log2(distance) static const int DISABLE_STATIC_JITTER_FRAMES = -1; static const float DEFAULT_NOISE_MUTING_THRESHOLD = 1.0f; diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index ed3a5b0541..8c47893aa3 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -18,6 +18,8 @@ #include #include +#include + #include "AudioMixerStats.h" #include "AudioMixerSlavePool.h" diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 2560f43337..d4d4f847ee 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AudioMixerClientData.h" + #include #include @@ -22,8 +24,6 @@ #include "AudioLogging.h" #include "AudioHelpers.h" #include "AudioMixer.h" -#include "AudioMixerClientData.h" - AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID) : NodeData(nodeID), diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index c3a31715ea..514e1c9756 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -21,6 +21,7 @@ #include #include +#include #include #include "PositionalAudioStream.h" diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index abc2f7220d..b975ba8bfe 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AudioMixerSlave.h" + #include #include @@ -34,8 +36,6 @@ #include "InjectedAudioStream.h" #include "AudioHelpers.h" -#include "AudioMixerSlave.h" - using AudioStreamMap = AudioMixerClientData::AudioStreamMap; // packet helpers diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 8907cf7858..8db0377f88 100644 Binary files a/interface/resources/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/qml/controls-uit/Separator.qml b/interface/resources/qml/controls-uit/Separator.qml index 5e2d278454..3350764ae9 100644 --- a/interface/resources/qml/controls-uit/Separator.qml +++ b/interface/resources/qml/controls-uit/Separator.qml @@ -14,8 +14,8 @@ import "../styles-uit" Item { property int colorScheme: 0; - readonly property var topColor: [ hifi.colors.baseGrayShadow, hifi.colors.faintGray ]; - readonly property var bottomColor: [ hifi.colors.baseGrayHighlight, hifi.colors.faintGray ]; + readonly property var topColor: [ hifi.colors.baseGrayShadow, hifi.colors.faintGray, "#89858C" ]; + readonly property var bottomColor: [ hifi.colors.baseGrayHighlight, hifi.colors.faintGray, "#89858C" ]; // Size height: colorScheme === 0 ? 2 : 1; diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index b061dc0a8e..bcc7672635 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -32,11 +32,12 @@ TextField { property string leftPermanentGlyph: ""; property string centerPlaceholderGlyph: ""; - FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } + placeholderText: textField.placeholderText + + FontLoader { id: firaSansRegular; source: "../../fonts/FiraSans-Regular.ttf"; } FontLoader { id: hifiGlyphs; source: "../../fonts/hifi-glyphs.ttf"; } - font.family: firaSansSemiBold.name + font.family: firaSansRegular.name font.pixelSize: hifi.fontSizes.textFieldInput - font.italic: textField.text == "" height: implicitHeight + 3 // Make surrounding box higher so that highlight is vertically centered. property alias textFieldLabel: textFieldLabel diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 809f48361d..ab47bb28ad 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -28,7 +28,7 @@ Rectangle { id: root; objectName: "checkout" property string activeView: "initialize"; - property bool purchasesReceived: false; + property bool ownershipStatusReceived: false; property bool balanceReceived: false; property string itemName; property string itemId; @@ -37,11 +37,16 @@ Rectangle { property double balanceAfterPurchase; property bool alreadyOwned: false; property int itemPrice: -1; - property bool itemIsJson: true; + property bool isCertified; + property string itemType; + property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar"]; + property var itemTypesText: ["entity", "wearable", "content set", "app", "avatar"]; + property var buttonTextNormal: ["REZ", "WEAR", "REPLACE CONTENT SET", "INSTALL", "WEAR"]; + property var buttonTextClicked: ["REZZED!", "WORN!", "CONTENT SET REPLACED!", "INSTALLED!", "AVATAR CHANGED!"] + property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar]; property bool shouldBuyWithControlledFailure: false; property bool debugCheckoutSuccess: false; property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); - property bool isWearable; property string referrer; // Style color: hifi.colors.white; @@ -85,7 +90,9 @@ Rectangle { UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message); } else { root.itemHref = result.data.download_url; - root.isWearable = result.data.categories.indexOf("Wearables") > -1; + if (result.data.categories.indexOf("Wearables") > -1) { + root.itemType = "wearable"; + } root.activeView = "checkoutSuccess"; UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned); } @@ -97,32 +104,53 @@ Rectangle { } else { root.balanceReceived = true; root.balanceAfterPurchase = result.data.balance - root.itemPrice; - root.setBuyText(); + root.refreshBuyUI(); } } - onInventoryResult: { + onAlreadyOwnedResult: { if (result.status !== 'success') { - console.log("Failed to get purchases", result.data.message); + console.log("Failed to get Already Owned status", result.data.message); } else { - root.purchasesReceived = true; - if (purchasesContains(result.data.assets, itemId)) { - root.alreadyOwned = true; + root.ownershipStatusReceived = true; + if (result.data.marketplace_item_id === root.itemId) { + root.alreadyOwned = result.data.already_owned; } else { + console.log("WARNING - Received 'Already Owned' status about different Marketplace ID!"); root.alreadyOwned = false; } - root.setBuyText(); + root.refreshBuyUI(); } } } onItemIdChanged: { - Commerce.inventory(); + root.ownershipStatusReceived = false; + Commerce.alreadyOwned(root.itemId); itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg"; } onItemHrefChanged: { - itemIsJson = root.itemHref.endsWith('.json'); + if (root.itemHref.indexOf(".fst") > -1) { + root.itemType = "avatar"; + } else if (root.itemHref.indexOf('.json.gz') > -1) { + root.itemType = "contentSet"; + } else if (root.itemHref.indexOf('.app.json') > -1) { + root.itemType = "app"; + } else if (root.itemHref.indexOf('.json') > -1) { + root.itemType = "entity"; // "wearable" type handled later + } else { + console.log("WARNING - Item type is UNKNOWN!"); + root.itemType = "entity"; + } + } + + onItemTypeChanged: { + if (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "avatar") { + root.isCertified = true; + } else { + root.isCertified = false; + } } onItemPriceChanged: { @@ -203,7 +231,7 @@ Rectangle { color: hifi.colors.white; Component.onCompleted: { - purchasesReceived = false; + ownershipStatusReceived = false; balanceReceived = false; Commerce.getWalletStatus(); } @@ -278,6 +306,31 @@ Rectangle { anchors.left: parent.left; anchors.right: parent.right; + Rectangle { + id: loading; + z: 997; + visible: !root.ownershipStatusReceived || !root.balanceReceived; + anchors.fill: parent; + color: Qt.rgba(0.0, 0.0, 0.0, 0.7); + + // This object is always used in a popup. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + propagateComposedEvents: false; + } + + AnimatedImage { + source: "../common/images/loader.gif" + width: 96; + height: width; + anchors.verticalCenter: parent.verticalCenter; + anchors.horizontalCenter: parent.horizontalCenter; + } + } + RalewayRegular { id: confirmPurchaseText; anchors.top: parent.top; @@ -286,8 +339,8 @@ Rectangle { anchors.leftMargin: 16; width: paintedWidth; height: paintedHeight; - text: "Confirm Purchase:"; - color: hifi.colors.baseGray; + text: "Review Purchase:"; + color: hifi.colors.black; size: 28; } @@ -400,7 +453,7 @@ Rectangle { width: root.width; // Anchors anchors.top: separator2.bottom; - anchors.topMargin: 16; + anchors.topMargin: 0; anchors.left: parent.left; anchors.leftMargin: 16; anchors.right: parent.right; @@ -411,8 +464,8 @@ Rectangle { Rectangle { id: buyTextContainer; visible: buyText.text !== ""; - anchors.top: cancelPurchaseButton.bottom; - anchors.topMargin: 16; + anchors.top: parent.top; + anchors.topMargin: 10; anchors.left: parent.left; anchors.right: parent.right; height: buyText.height + 30; @@ -454,32 +507,63 @@ Rectangle { // Alignment horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; + } + } - onLinkActivated: { - sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName}); - } + // "View in My Purchases" button + HifiControlsUit.Button { + id: viewInMyPurchasesButton; + visible: false; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.light; + anchors.top: buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top; + anchors.topMargin: 10; + height: 50; + anchors.left: parent.left; + anchors.right: parent.right; + text: "VIEW THIS ITEM IN MY PURCHASES"; + onClicked: { + sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName}); } } // "Buy" button HifiControlsUit.Button { id: buyButton; - enabled: (root.balanceAfterPurchase >= 0 && purchasesReceived && balanceReceived) || !itemIsJson; - color: hifi.buttons.blue; + visible: !((root.itemType === "avatar" || root.itemType === "app") && viewInMyPurchasesButton.visible) + enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived) || (!root.isCertified); + color: viewInMyPurchasesButton.visible ? hifi.buttons.white : hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; - anchors.top: checkoutActionButtonsContainer.top; - anchors.topMargin: 16; - height: 40; + anchors.top: viewInMyPurchasesButton.visible ? viewInMyPurchasesButton.bottom : + (buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top); + anchors.topMargin: 10; + height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: (itemIsJson ? ((purchasesReceived && balanceReceived) ? "Confirm Purchase" : "--") : "Get Item"); + text: ((root.isCertified) ? ((ownershipStatusReceived && balanceReceived) ? + (viewInMyPurchasesButton.visible ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item"); onClicked: { - if (itemIsJson) { - buyButton.enabled = false; + if (root.isCertified) { if (!root.shouldBuyWithControlledFailure) { - Commerce.buy(itemId, itemPrice); + if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { + lightboxPopup.titleText = "Purchase Content Set"; + lightboxPopup.bodyText = "You will not be able to replace this domain's content with " + root.itemName + + " until the server owner gives you 'Replace Content' permissions.

Are you sure you want to purchase this content set?"; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "Commerce.buy('" + root.itemId + "', " + root.itemPrice + ");" + + "root.visible = false; buyButton.enabled = false; loading.visible = true;"; + lightboxPopup.visible = true; + } else { + buyButton.enabled = false; + loading.visible = true; + Commerce.buy(root.itemId, root.itemPrice); + } } else { - Commerce.buy(itemId, itemPrice, true); + buyButton.enabled = false; + loading.visible = true; + Commerce.buy(root.itemId, root.itemPrice, true); } } else { if (urlHandler.canHandleUrl(itemHref)) { @@ -494,9 +578,9 @@ Rectangle { id: cancelPurchaseButton; color: hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.light; - anchors.top: buyButton.bottom; - anchors.topMargin: 16; - height: 40; + anchors.top: buyButton.visible ? buyButton.bottom : viewInMyPurchasesButton.bottom; + anchors.topMargin: 10; + height: 50; anchors.left: parent.left; anchors.right: parent.right; text: "Cancel" @@ -522,31 +606,32 @@ Rectangle { anchors.top: titleBarContainer.bottom; anchors.bottom: root.bottom; anchors.left: parent.left; - anchors.leftMargin: 16; + anchors.leftMargin: 20; anchors.right: parent.right; - anchors.rightMargin: 16; + anchors.rightMargin: 20; RalewayRegular { id: completeText; anchors.top: parent.top; - anchors.topMargin: 30; + anchors.topMargin: 18; anchors.left: parent.left; width: paintedWidth; height: paintedHeight; text: "Thank you for your order!"; color: hifi.colors.baseGray; - size: 28; + size: 36; } RalewaySemiBold { id: completeText2; - text: "The item " + '' + root.itemName + '' + - " has been added to your Purchases and a receipt will appear in your Wallet's transaction history."; + text: "The " + (root.itemTypesText)[itemTypesArray.indexOf(root.itemType)] + + ' ' + root.itemName + '' + + " has been added to your Purchases and a receipt will appear in your Wallet's transaction history."; // Text size - size: 20; + size: 18; // Anchors anchors.top: completeText.bottom; - anchors.topMargin: 10; + anchors.topMargin: 15; height: paintedHeight; anchors.left: parent.left; anchors.right: parent.right; @@ -576,7 +661,7 @@ Rectangle { RalewayBold { anchors.fill: parent; - text: "REZZED"; + text: (root.buttonTextClicked)[itemTypesArray.indexOf(root.itemType)]; size: 18; color: hifi.colors.white; verticalAlignment: Text.AlignVCenter; @@ -592,26 +677,52 @@ Rectangle { // "Rez" button HifiControlsUit.Button { id: rezNowButton; - enabled: root.canRezCertifiedItems || root.isWearable; - buttonGlyph: hifi.glyphs.lightning; + enabled: (root.itemType === "entity" && root.canRezCertifiedItems) || + (root.itemType === "contentSet" && Entities.canReplaceContent()) || + root.itemType === "wearable" || root.itemType === "avatar"; + buttonGlyph: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)]; color: hifi.buttons.red; colorScheme: hifi.colorSchemes.light; anchors.top: completeText2.bottom; - anchors.topMargin: 30; + anchors.topMargin: 27; height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: root.isWearable ? "Wear It" : "Rez It" + text: (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; onClicked: { - sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); - rezzedNotifContainer.visible = true; - rezzedNotifContainerTimer.start(); - UserActivityLogger.commerceEntityRezzed(root.itemId, "checkout", root.isWearable ? "rez" : "wear"); + if (root.itemType === "contentSet") { + lightboxPopup.titleText = "Replace Content"; + lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain. " + + "If you want to save the state of the content in this domain, create a backup before proceeding.

" + + "For more information about backing up and restoring content, " + + "" + + "click here to open info on your desktop browser."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemHref + "');" + + "root.visible = false;rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start();" + + "UserActivityLogger.commerceEntityRezzed('" + root.itemId + "', 'checkout', '" + root.itemType + "');"; + lightboxPopup.visible = true; + } else if (root.itemType === "avatar") { + lightboxPopup.titleText = "Change Avatar"; + lightboxPopup.bodyText = "This will change your current avatar to " + root.itemName + " while retaining your wearables."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "MyAvatar.useFullAvatarURL('" + root.itemHref + "'); root.visible = false;"; + lightboxPopup.visible = true; + } else { + sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); + rezzedNotifContainer.visible = true; + rezzedNotifContainerTimer.start(); + UserActivityLogger.commerceEntityRezzed(root.itemId, "checkout", root.itemType); + } } } RalewaySemiBold { id: noPermissionText; - visible: !root.canRezCertifiedItems && !root.isWearable; + visible: !root.canRezCertifiedItems && root.itemType === "entity"; text: 'You do not have Certified Rez permissions in this domain.' // Text size size: 16; @@ -640,7 +751,7 @@ Rectangle { } RalewaySemiBold { id: explainRezText; - visible: !root.isWearable; + visible: root.itemType === "entity"; text: 'What does "Rez" mean?' // Text size size: 16; @@ -663,9 +774,9 @@ Rectangle { RalewaySemiBold { id: myPurchasesLink; - text: 'View this item in My Purchases'; + text: 'View this item in My Purchases'; // Text size - size: 20; + size: 18; // Anchors anchors.top: explainRezText.visible ? explainRezText.bottom : (noPermissionText.visible ? noPermissionText.bottom : rezNowButton.bottom); anchors.topMargin: 40; @@ -685,12 +796,12 @@ Rectangle { RalewaySemiBold { id: walletLink; - text: 'View receipt in Wallet'; + text: 'View receipt in Wallet'; // Text size - size: 20; + size: 18; // Anchors anchors.top: myPurchasesLink.bottom; - anchors.topMargin: 20; + anchors.topMargin: 16; height: paintedHeight; anchors.left: parent.left; anchors.right: parent.right; @@ -708,12 +819,12 @@ Rectangle { RalewayRegular { id: pendingText; text: 'Your item is marked "pending" while your purchase is being confirmed. ' + - 'Learn More'; + 'Learn More'; // Text size - size: 20; + size: 18; // Anchors anchors.top: walletLink.bottom; - anchors.topMargin: 60; + anchors.topMargin: 32; height: paintedHeight; anchors.left: parent.left; anchors.right: parent.right; @@ -739,11 +850,10 @@ Rectangle { color: hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.light; anchors.bottom: parent.bottom; - anchors.bottomMargin: 20; + anchors.bottomMargin: 54; anchors.right: parent.right; - anchors.rightMargin: 14; - width: parent.width/2 - anchors.rightMargin; - height: 60; + width: 193; + height: 44; text: "Continue Shopping"; onClicked: { sendToScript({method: 'checkout_continueShopping', itemId: itemId}); @@ -851,7 +961,7 @@ Rectangle { buyButton.color = hifi.buttons.red; root.shouldBuyWithControlledFailure = true; } else { - buyButton.text = (itemIsJson ? ((purchasesReceived && balanceReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); + buyButton.text = (root.isCertified ? ((ownershipStatusReceived && balanceReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); buyButton.color = hifi.buttons.blue; root.shouldBuyWithControlledFailure = false; } @@ -883,7 +993,7 @@ Rectangle { itemHref = message.params.itemHref; referrer = message.params.referrer; itemAuthor = message.params.itemAuthor; - setBuyText(); + refreshBuyUI(); break; default: console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); @@ -891,22 +1001,13 @@ Rectangle { } signal sendToScript(var message); - function purchasesContains(purchasesJson, id) { - for (var idx = 0; idx < purchasesJson.length; idx++) { - if(purchasesJson[idx].id === id) { - return true; - } - } - return false; - } - - function setBuyText() { - if (root.itemIsJson) { - if (root.purchasesReceived && root.balanceReceived) { + function refreshBuyUI() { + if (root.isCertified) { + if (root.ownershipStatusReceived && root.balanceReceived) { if (root.balanceAfterPurchase < 0) { if (root.alreadyOwned) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item again.
" + - 'View the copy you own in My Purchases
'; + buyText.text = "Your Wallet does not have sufficient funds to purchase this item again."; + viewInMyPurchasesButton.visible = true; } else { buyText.text = "Your Wallet does not have sufficient funds to purchase this item."; } @@ -916,15 +1017,19 @@ Rectangle { buyGlyph.size = 54; } else { if (root.alreadyOwned) { - buyText.text = 'You already own this item.
Purchasing it will buy another copy.
View this item in My Purchases
'; - buyTextContainer.color = "#FFD6AD"; - buyTextContainer.border.color = "#FAC07D"; - buyGlyph.text = hifi.glyphs.alert; - buyGlyph.size = 46; + viewInMyPurchasesButton.visible = true; } else { buyText.text = ""; } + + if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { + buyText.text = "The domain owner must enable 'Replace Content' permissions for you in this " + + "domain's server settings before you can replace this domain's content with " + root.itemName + ""; + buyTextContainer.color = "#FFC3CD"; + buyTextContainer.border.color = "#F3808F"; + buyGlyph.text = hifi.glyphs.alert; + buyGlyph.size = 54; + } } } else { buyText.text = ""; @@ -945,8 +1050,8 @@ Rectangle { root.activeView = "checkoutSuccess"; } root.balanceReceived = false; - root.purchasesReceived = false; - Commerce.inventory(); + root.ownershipStatusReceived = false; + Commerce.alreadyOwned(root.itemId); Commerce.balance(); } diff --git a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml index 4f18a934ee..bb4f5c0c92 100644 --- a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml +++ b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml @@ -99,6 +99,10 @@ Rectangle { size: 20; verticalAlignment: Text.AlignTop; wrapMode: Text.WordWrap; + + onLinkActivated: { + sendToParent({ method: 'commerceLightboxLinkClicked', linkUrl: link }); + } } Item { diff --git a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml index 32650342a1..5f874d3f04 100644 --- a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml +++ b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml @@ -89,11 +89,11 @@ Rectangle { id: introText2; text: "My Purchases"; // Text size - size: 28; + size: 22; // Anchors anchors.top: introText1.bottom; anchors.left: parent.left; - anchors.leftMargin: 12; + anchors.leftMargin: 24; anchors.right: parent.right; height: paintedHeight; // Style diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index f1bf23c983..8ef5902905 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -207,6 +207,7 @@ Rectangle { // able to click on a button/mouseArea underneath the popup/section. MouseArea { anchors.fill: parent; + hoverEnabled: true; propagateComposedEvents: false; } diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 05e280b839..cc2bcd69aa 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -20,6 +20,7 @@ import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet +import TabletScriptingInterface 1.0 // references XXX from root context @@ -29,7 +30,6 @@ Item { id: root; property string purchaseStatus; property bool purchaseStatusChanged; - property bool canRezCertifiedItems: false; property string itemName; property string itemId; property string itemPreviewImageUrl; @@ -39,7 +39,14 @@ Item { property int itemEdition; property int numberSold; property int limitedRun; - property bool isWearable; + property string itemType; + property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar"]; + property var buttonTextNormal: ["REZ", "WEAR", "REPLACE", "INSTALL", "WEAR"]; + property var buttonTextClicked: ["REZZED", "WORN", "REPLACED", "INSTALLED", "WORN"] + property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar]; + property bool showConfirmation: false; + property bool hasPermissionToRezThis; + property bool permissionExplanationCardVisible; property string originalStatusText; property string originalStatusColor; @@ -47,6 +54,35 @@ Item { height: 110; width: parent.width; + Connections { + target: Commerce; + + onContentSetChanged: { + if (contentSetHref === root.itemHref) { + showConfirmation = true; + } + } + } + + Connections { + target: MyAvatar; + + onSkeletonModelURLChanged: { + if (skeletonModelURL === root.itemHref) { + showConfirmation = true; + } + } + } + + onItemTypeChanged: { + if ((itemType === "entity" && (!Entities.canRezCertified() && !Entities.canRezTmpCertified())) || + (itemType === "contentSet" && !Entities.canReplaceContent())) { + root.hasPermissionToRezThis = false; + } else { + root.hasPermissionToRezThis = true; + } + } + onPurchaseStatusChangedChanged: { if (root.purchaseStatusChanged === true && root.purchaseStatus === "confirmed") { root.originalStatusText = statusText.text; @@ -57,6 +93,15 @@ Item { } } + onShowConfirmationChanged: { + if (root.showConfirmation) { + rezzedNotifContainer.visible = true; + rezzedNotifContainerTimer.start(); + UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.itemType); + root.showConfirmation = false; + } + } + Timer { id: confirmedTimer; interval: 3000; @@ -73,10 +118,10 @@ Item { color: hifi.colors.white; // Size anchors.left: parent.left; - anchors.leftMargin: 8; + anchors.leftMargin: 16; anchors.right: parent.right; - anchors.rightMargin: 8; - anchors.top: parent.top; + anchors.rightMargin: 16; + anchors.verticalCenter: parent.verticalCenter; height: root.height - 10; Image { @@ -96,15 +141,20 @@ Item { } } - + TextMetrics { + id: itemNameTextMetrics; + font: itemName.font; + text: itemName.text; + } RalewaySemiBold { id: itemName; anchors.top: itemPreviewImage.top; anchors.topMargin: 4; anchors.left: itemPreviewImage.right; anchors.leftMargin: 8; - anchors.right: buttonContainer.left; - anchors.rightMargin: 8; + width: !noPermissionGlyph.visible ? (buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width - anchors.leftMargin) : + Math.min(itemNameTextMetrics.tightBoundingRect.width + 2, + buttonContainer.x - itemPreviewImage.x - itemPreviewImage.width - anchors.leftMargin - noPermissionGlyph.width + 2); height: paintedHeight; // Text size size: 24; @@ -130,6 +180,93 @@ Item { } } } + HiFiGlyphs { + id: noPermissionGlyph; + visible: !root.hasPermissionToRezThis; + anchors.verticalCenter: itemName.verticalCenter; + anchors.left: itemName.right; + anchors.leftMargin: itemName.truncated ? -10 : -2; + text: hifi.glyphs.info; + // Size + size: 40; + width: 32; + // Style + color: hifi.colors.redAccent; + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + + onEntered: { + noPermissionGlyph.color = hifi.colors.redHighlight; + } + onExited: { + noPermissionGlyph.color = hifi.colors.redAccent; + } + onClicked: { + root.sendToPurchases({ method: 'openPermissionExplanationCard' }); + } + } + } + Rectangle { + id: permissionExplanationCard; + z: 995; + visible: root.permissionExplanationCardVisible; + anchors.fill: parent; + color: hifi.colors.white; + + RalewayRegular { + id: permissionExplanationText; + text: { + if (root.itemType === "contentSet") { + "You do not have 'Replace Content' permissions in this domain. Learn more"; + } else if (root.itemType === "entity") { + "You do not have 'Rez Certified' permissions in this domain. Learn more"; + } else { + "" + } + } + size: 16; + anchors.left: parent.left; + anchors.leftMargin: 30; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.right: permissionExplanationGlyph.left; + color: hifi.colors.baseGray; + wrapMode: Text.WordWrap; + verticalAlignment: Text.AlignVCenter; + + onLinkActivated: { + sendToPurchases({method: 'showPermissionsExplanation', itemType: root.itemType}); + } + } + // "Close" button + HiFiGlyphs { + id: permissionExplanationGlyph; + text: hifi.glyphs.close; + color: hifi.colors.baseGray; + size: 26; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.right: parent.right; + width: 77; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.text = hifi.glyphs.closeInverted; + } + onExited: { + parent.text = hifi.glyphs.close; + } + onClicked: { + root.sendToPurchases({ method: 'openPermissionExplanationCard', closeAll: true }); + } + } + } + } Item { id: certificateContainer; @@ -151,19 +288,19 @@ Item { anchors.bottom: parent.bottom; width: 32; // Style - color: hifi.colors.lightGray; + color: hifi.colors.black; } RalewayRegular { id: viewCertificateText; text: "VIEW CERTIFICATE"; - size: 14; + size: 13; anchors.left: certificateIcon.right; anchors.leftMargin: 4; anchors.top: parent.top; anchors.bottom: parent.bottom; anchors.right: parent.right; - color: hifi.colors.lightGray; + color: hifi.colors.black; } MouseArea { @@ -173,13 +310,13 @@ Item { sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId}); } onEntered: { - certificateIcon.color = hifi.colors.black; - viewCertificateText.color = hifi.colors.black; - } - onExited: { certificateIcon.color = hifi.colors.lightGray; viewCertificateText.color = hifi.colors.lightGray; } + onExited: { + certificateIcon.color = hifi.colors.black; + viewCertificateText.color = hifi.colors.black; + } } } @@ -193,14 +330,14 @@ Item { anchors.right: buttonContainer.left; anchors.rightMargin: 2; - FiraSansRegular { + RalewayRegular { anchors.left: parent.left; anchors.top: parent.top; anchors.bottom: parent.bottom; width: paintedWidth; text: "#" + root.itemEdition; - size: 15; - color: "#cc6a6a6a"; + size: 13; + color: hifi.colors.black; verticalAlignment: Text.AlignTop; } } @@ -311,7 +448,7 @@ Item { id: rezzedNotifContainer; z: 998; visible: false; - color: hifi.colors.blueHighlight; + color: "#1FC6A6"; anchors.fill: buttonContainer; MouseArea { anchors.fill: parent; @@ -321,8 +458,8 @@ Item { RalewayBold { anchors.fill: parent; - text: "REZZED"; - size: 18; + text: (root.buttonTextClicked)[itemTypesArray.indexOf(root.itemType)]; + size: 15; color: hifi.colors.white; verticalAlignment: Text.AlignVCenter; horizontalAlignment: Text.AlignHCenter; @@ -337,25 +474,47 @@ Item { Button { id: buttonContainer; - property int color: hifi.buttons.red; + property int color: hifi.buttons.blue; property int colorScheme: hifi.colorSchemes.light; anchors.top: parent.top; + anchors.topMargin: 4; anchors.bottom: parent.bottom; + anchors.bottomMargin: 4; anchors.right: parent.right; + anchors.rightMargin: 4; width: height; - enabled: (root.canRezCertifiedItems || root.isWearable) && root.purchaseStatus !== "invalidated"; + enabled: root.hasPermissionToRezThis && + root.purchaseStatus !== "invalidated" && + MyAvatar.skeletonModelURL !== root.itemHref; + + onHoveredChanged: { + if (hovered) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + onFocusChanged: { + if (focus) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } onClicked: { - sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); - rezzedNotifContainer.visible = true; - rezzedNotifContainerTimer.start(); - UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.isWearable ? "rez" : "wear"); + Tablet.playSound(TabletEnums.ButtonClick); + if (root.itemType === "contentSet") { + sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref}); + } else if (root.itemType === "avatar") { + sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref}); + } else { + sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); + root.showConfirmation = true; + } } style: ButtonStyle { - background: Rectangle { + radius: 4; gradient: Gradient { GradientStop { position: 0.2 @@ -390,13 +549,13 @@ Item { label: Item { HiFiGlyphs { - id: lightningIcon; - text: hifi.glyphs.lightning; + id: rezIcon; + text: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)]; // Size - size: 32; + size: 60; // Anchors anchors.top: parent.top; - anchors.topMargin: 12; + anchors.topMargin: 0; anchors.left: parent.left; anchors.right: parent.right; horizontalAlignment: Text.AlignHCenter; @@ -405,18 +564,19 @@ Item { : hifi.buttons.disabledTextColor[control.colorScheme] } RalewayBold { - anchors.top: lightningIcon.bottom; - anchors.topMargin: -20; + id: rezIconLabel; + anchors.top: rezIcon.bottom; + anchors.topMargin: -4; anchors.right: parent.right; anchors.left: parent.left; anchors.bottom: parent.bottom; font.capitalization: Font.AllUppercase color: enabled ? hifi.buttons.textColor[control.color] : hifi.buttons.disabledTextColor[control.colorScheme] - size: 16; + size: 15; verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - text: root.isWearable ? "Wear It" : "Rez It" + text: MyAvatar.skeletonModelURL === root.itemHref ? "CURRENT" : (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; } } } @@ -425,11 +585,11 @@ Item { DropShadow { anchors.fill: mainContainer; - horizontalOffset: 3; - verticalOffset: 3; - radius: 8.0; - samples: 17; - color: "#80000000"; + horizontalOffset: 0; + verticalOffset: 4; + radius: 4.0; + samples: 9 + color: Qt.rgba(0, 0, 0, 0.25); source: mainContainer; } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 3be8d882cd..54e7c9c2dd 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -31,7 +31,6 @@ Rectangle { property bool securityImageResultReceived: false; property bool purchasesReceived: false; property bool punctuationMode: false; - property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); property bool pendingInventoryReply: true; property bool isShowingMyItems: false; property bool isDebuggingFirstUseTutorial: false; @@ -147,7 +146,11 @@ Rectangle { Connections { onSendToParent: { - sendToScript(msg); + if (msg.method === 'commerceLightboxLinkClicked') { + Qt.openUrlExternally(msg.linkUrl); + } else { + sendToScript(msg); + } } } } @@ -296,7 +299,7 @@ Rectangle { anchors.left: parent.left; anchors.leftMargin: 8; anchors.right: parent.right; - anchors.rightMargin: 12; + anchors.rightMargin: 16; anchors.top: parent.top; anchors.topMargin: 4; @@ -307,11 +310,11 @@ Rectangle { anchors.bottom: parent.bottom; anchors.bottomMargin: 10; anchors.left: parent.left; - anchors.leftMargin: 4; + anchors.leftMargin: 16; width: paintedWidth; text: isShowingMyItems ? "My Items" : "My Purchases"; - color: hifi.colors.baseGray; - size: 28; + color: hifi.colors.black; + size: 22; } HifiControlsUit.TextField { @@ -322,8 +325,8 @@ Rectangle { hasRoundedBorder: true; anchors.left: myText.right; anchors.leftMargin: 16; - anchors.top: parent.top; - anchors.bottom: parent.bottom; + height: 39; + anchors.verticalCenter: parent.verticalCenter; anchors.right: parent.right; placeholderText: "filter items"; @@ -344,7 +347,7 @@ Rectangle { HifiControlsUit.Separator { id: separator; - colorScheme: 1; + colorScheme: 2; anchors.left: parent.left; anchors.right: parent.right; anchors.top: filterBarContainer.bottom; @@ -364,69 +367,6 @@ Rectangle { id: filteredPurchasesModel; } - Rectangle { - id: cantRezCertified; - visible: !root.canRezCertifiedItems; - color: "#FFC3CD"; - radius: 4; - border.color: hifi.colors.redAccent; - border.width: 1; - anchors.top: separator.bottom; - anchors.topMargin: 12; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - height: 80; - - HiFiGlyphs { - id: lightningIcon; - text: hifi.glyphs.lightning; - // Size - size: 36; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 18; - anchors.left: parent.left; - anchors.leftMargin: 12; - horizontalAlignment: Text.AlignHCenter; - // Style - color: hifi.colors.lightGray; - } - - RalewayRegular { - text: "You don't have permission to rez certified items in this domain. " + - 'Learn More'; - // Text size - size: 18; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 4; - anchors.left: lightningIcon.right; - anchors.leftMargin: 8; - anchors.right: parent.right; - anchors.rightMargin: 8; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 4; - // Style - color: hifi.colors.baseGray; - wrapMode: Text.WordWrap; - // Alignment - verticalAlignment: Text.AlignVCenter; - - onLinkActivated: { - lightboxPopup.titleText = "Rez Permission Required"; - lightboxPopup.bodyText = "You don't have permission to rez certified items in this domain.

" + - "Use the GOTO app to visit another domain or go to your own sandbox."; - lightboxPopup.button1text = "CLOSE"; - lightboxPopup.button1method = "root.visible = false;" - lightboxPopup.button2text = "OPEN GOTO"; - lightboxPopup.button2method = "sendToParent({method: 'purchases_openGoTo'});"; - lightboxPopup.visible = true; - } - } - } - ListView { id: purchasesContentsList; visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0); @@ -435,13 +375,12 @@ Rectangle { snapMode: ListView.SnapToItem; highlightRangeMode: ListView.StrictlyEnforceRange; // Anchors - anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom; + anchors.top: separator.bottom; anchors.topMargin: 12; anchors.left: parent.left; anchors.bottom: parent.bottom; width: parent.width; delegate: PurchasedItem { - canRezCertifiedItems: root.canRezCertifiedItems; itemName: title; itemId: id; itemPreviewImageUrl: preview; @@ -453,16 +392,31 @@ Rectangle { numberSold: model.number_sold; limitedRun: model.limited_run; displayedItemCount: model.displayedItemCount; - isWearable: model.categories.indexOf("Wearables") > -1; - anchors.topMargin: 12; - anchors.bottomMargin: 12; + permissionExplanationCardVisible: model.permissionExplanationCardVisible; + itemType: { + if (model.root_file_url.indexOf(".fst") > -1) { + "avatar"; + } else if (model.categories.indexOf("Wearables") > -1) { + "wearable"; + } else if (model.root_file_url.endsWith('.json.gz')) { + "contentSet"; + } else if (model.root_file_url.endsWith('.app.json')) { + "app"; + } else if (model.root_file_url.endsWith('.json')) { + "entity"; + } else { + "unknown"; + } + } + anchors.topMargin: 10; + anchors.bottomMargin: 10; Connections { onSendToPurchases: { if (msg.method === 'purchases_itemInfoClicked') { sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId}); } else if (msg.method === "purchases_rezClicked") { - sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, isWearable: isWearable}); + sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, itemType: itemType}); } else if (msg.method === 'purchases_itemCertificateClicked') { inspectionCertificate.visible = true; inspectionCertificate.isLightbox = true; @@ -481,8 +435,51 @@ Rectangle { lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = "root.visible = false;" lightboxPopup.visible = true; + } else if (msg.method === "showReplaceContentLightbox") { + lightboxPopup.titleText = "Replace Content"; + lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain. " + + "If you want to save the state of the content in this domain, create a backup before proceeding.

" + + "For more information about backing up and restoring content, " + + "" + + "click here to open info on your desktop browser."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemHref + "'); root.visible = false;"; + lightboxPopup.visible = true; + } else if (msg.method === "showChangeAvatarLightbox") { + lightboxPopup.titleText = "Change Avatar"; + lightboxPopup.bodyText = "This will change your current avatar to " + msg.itemName + " while retaining your wearables."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = "MyAvatar.useFullAvatarURL('" + msg.itemHref + "'); root.visible = false;"; + lightboxPopup.visible = true; + } else if (msg.method === "showPermissionsExplanation") { + if (msg.itemType === "entity") { + lightboxPopup.titleText = "Rez Certified Permission"; + lightboxPopup.bodyText = "You don't have permission to rez certified items in this domain.

" + + "Use the GOTO app to visit another domain or go to your own sandbox."; + lightboxPopup.button2text = "OPEN GOTO"; + lightboxPopup.button2method = "sendToParent({method: 'purchases_openGoTo'});"; + } else if (msg.itemType === "contentSet") { + lightboxPopup.titleText = "Replace Content Permission"; + lightboxPopup.bodyText = "You do not have the permission 'Replace Content' in this domain's server settings. The domain owner " + + "must enable it for you before you can replace content sets in this domain."; + } + lightboxPopup.button1text = "CLOSE"; + lightboxPopup.button1method = "root.visible = false;" + lightboxPopup.visible = true; } else if (msg.method === "setFilterText") { filterBar.text = msg.filterText; + } else if (msg.method === "openPermissionExplanationCard") { + for (var i = 0; i < filteredPurchasesModel.count; i++) { + if (i !== index || msg.closeAll) { + filteredPurchasesModel.setProperty(i, "permissionExplanationCardVisible", false); + } else { + filteredPurchasesModel.setProperty(i, "permissionExplanationCardVisible", true); + } + } } } } @@ -684,6 +681,7 @@ Rectangle { filteredPurchasesModel.clear(); for (var i = 0; i < tempPurchasesModel.count; i++) { filteredPurchasesModel.append(tempPurchasesModel.get(i)); + filteredPurchasesModel.setProperty(i, 'permissionExplanationCardVisible', false); } populateDisplayedItemCounts(); diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index d14f664034..1a27391572 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -74,8 +74,6 @@ Item { // TODO: Fix this unlikely bug onVisibleChanged: { if (visible) { - passphraseField.error = false; - passphraseField.focus = true; sendSignalToParent({method: 'disableHmdPreview'}); } else { sendSignalToParent({method: 'maybeEnableHmdPreview'}); @@ -205,6 +203,14 @@ Item { placeholderText: "passphrase"; activeFocusOnPress: true; activeFocusOnTab: true; + + onVisibleChanged: { + if (visible) { + error = false; + focus = true; + forceActiveFocus(); + } + } onFocusChanged: { root.keyboardRaised = focus; diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml index 7dd72b904e..f5b7f42a3f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml @@ -923,7 +923,7 @@ Item { anchors.leftMargin: 20; anchors.right: parent.right; anchors.rightMargin: 20; - height: 140; + height: 95; FontLoader { id: firaSansSemiBold; source: "../../../../../fonts/FiraSans-SemiBold.ttf"; } TextArea { @@ -947,8 +947,14 @@ Item { wrapMode: TextEdit.Wrap; activeFocusOnPress: true; activeFocusOnTab: true; - // Workaround for no max length on TextAreas onTextChanged: { + // Don't allow tabs or newlines + if ((/[\n\r\t]/g).test(text)) { + var cursor = cursorPosition; + text = text.replace(/[\n\r\t]/g, ''); + cursorPosition = cursor-1; + } + // Workaround for no max length on TextAreas if (text.length > maximumLength) { var cursor = cursorPosition; text = previousText; diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 049a7faad8..bbecb9e85b 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -199,7 +199,7 @@ Rectangle { var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; - var SHAPE_TYPES = [];ww + var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index c79c05a601..e419b04848 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtQuick.Controls 2.2 // Need both for short-term fix import QtWebEngine 1.1 import QtWebChannel 1.0 import QtQuick.Controls.Styles 1.4 @@ -10,6 +11,7 @@ import "../../controls-uit" as HifiControls import "../../styles-uit" + TabView { id: editTabView // anchors.fill: parent @@ -23,8 +25,27 @@ TabView { Rectangle { color: "#404040" + id: container + + Flickable { + height: parent.height + width: parent.width + + contentHeight: createEntitiesFlow.height + importButton.height + assetServerButton.height + + header.anchors.topMargin + createEntitiesFlow.anchors.topMargin + + assetServerButton.anchors.topMargin + importButton.anchors.topMargin + contentWidth: width + + ScrollBar.vertical : ScrollBar { + visible: parent.contentHeight > parent.height + width: 20 + background: Rectangle { + color: hifi.colors.tableScrollBackgroundDark + } + } Text { + id: header color: "#ffffff" text: "Choose an Entity Type to Create:" font.pixelSize: 14 @@ -176,6 +197,7 @@ TabView { } HifiControls.Button { + id: importButton text: "Import Entities (.json)" color: hifi.buttons.black colorScheme: hifi.colorSchemes.dark @@ -192,6 +214,7 @@ TabView { } } } + } // Flickable } Tab { diff --git a/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml index e49d78193b..6df97e67b0 100644 --- a/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml @@ -131,7 +131,7 @@ Rectangle { spacing: 5 anchors.horizontalCenter: column3.horizontalCenter - anchors.horizontalCenterOffset: -20 + anchors.horizontalCenterOffset: 0 Button { id: button1 diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index cfff3a3273..1922b02f93 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -130,6 +130,7 @@ Item { flickableDirection: Flickable.AutoFlickIfNeeded keyNavigationEnabled: false highlightFollowsCurrentItem: false + interactive: false property int previousGridIndex: -1 diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 5da587ea57..43de8333af 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -218,7 +218,7 @@ Item { readonly property var colorStart: [ colors.white, colors.primaryHighlight, "#d42043", "#343434", Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] readonly property var colorFinish: [ colors.lightGrayText, colors.blueAccent, "#94132e", colors.black, Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] readonly property var hoveredColor: [ colorStart[white], colorStart[blue], colorStart[red], colorFinish[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] - readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] + readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colors.lightGrayText ] readonly property var focusedColor: [ colors.lightGray50, colors.blueAccent, colors.redAccent, colors.darkGray, colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] readonly property var disabledColorStart: [ colorStart[white], colors.baseGrayHighlight] readonly property var disabledColorFinish: [ colorFinish[white], colors.baseGrayShadow] @@ -354,5 +354,9 @@ Item { readonly property string wallet: "\ue027" readonly property string paperPlane: "\ue028" readonly property string passphrase: "\ue029" + readonly property string globe: "\ue02c" + readonly property string wand: "\ue02d" + readonly property string hat: "\ue02e" + readonly property string install: "\ue02f" } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3aa0f3d889..60e4c7c948 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4829,7 +4829,6 @@ void Application::setKeyboardFocusHighlight(const glm::vec3& position, const glm if (_keyboardFocusHighlightID == UNKNOWN_OVERLAY_ID || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { _keyboardFocusHighlight = std::make_shared(); _keyboardFocusHighlight->setAlpha(1.0f); - _keyboardFocusHighlight->setBorderSize(1.0f); _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); _keyboardFocusHighlight->setIsSolid(false); _keyboardFocusHighlight->setPulseMin(0.5); @@ -5969,7 +5968,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe DependencyManager::get().data()->setToolbarScriptingInterface(toolbarScriptingInterface); scriptEngine->registerGlobalObject("Window", DependencyManager::get().data()); - qScriptRegisterMetaType(scriptEngine.data(), CustomPromptResultToScriptValue, CustomPromptResultFromScriptValue); scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, LocationScriptingInterface::locationSetter, "Window"); // register `location` on the global object. @@ -6286,6 +6284,24 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) { return true; } +void Application::replaceDomainContent(const QString& url) { + qCDebug(interfaceapp) << "Attempting to replace domain content: " << url; + QByteArray urlData(url.toUtf8()); + auto limitedNodeList = DependencyManager::get(); + limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) { + return node->getType() == NodeType::EntityServer && node->getActiveSocket(); + }, [&urlData, limitedNodeList](const SharedNodePointer& octreeNode) { + auto octreeFilePacket = NLPacket::create(PacketType::OctreeFileReplacementFromUrl, urlData.size(), true); + octreeFilePacket->write(urlData); + limitedNodeList->sendPacket(std::move(octreeFilePacket), *octreeNode); + }); + auto addressManager = DependencyManager::get(); + addressManager->handleLookupString(DOMAIN_SPAWNING_POINT); + QString newHomeAddress = addressManager->getHost() + DOMAIN_SPAWNING_POINT; + qCDebug(interfaceapp) << "Setting new home bookmark to: " << newHomeAddress; + DependencyManager::get()->setHomeLocationToAddress(newHomeAddress); +} + bool Application::askToReplaceDomainContent(const QString& url) { QString methodDetails; const int MAX_CHARACTERS_PER_LINE = 90; @@ -6305,21 +6321,7 @@ bool Application::askToReplaceDomainContent(const QString& url) { QString details; if (static_cast(answer.toInt()) == QMessageBox::Yes) { // Given confirmation, send request to domain server to replace content - qCDebug(interfaceapp) << "Attempting to replace domain content: " << url; - QByteArray urlData(url.toUtf8()); - auto limitedNodeList = DependencyManager::get(); - limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) { - return node->getType() == NodeType::EntityServer && node->getActiveSocket(); - }, [&urlData, limitedNodeList](const SharedNodePointer& octreeNode) { - auto octreeFilePacket = NLPacket::create(PacketType::OctreeFileReplacementFromUrl, urlData.size(), true); - octreeFilePacket->write(urlData); - limitedNodeList->sendPacket(std::move(octreeFilePacket), *octreeNode); - }); - auto addressManager = DependencyManager::get(); - addressManager->handleLookupString(DOMAIN_SPAWNING_POINT); - QString newHomeAddress = addressManager->getHost() + DOMAIN_SPAWNING_POINT; - qCDebug(interfaceapp) << "Setting new home bookmark to: " << newHomeAddress; - DependencyManager::get()->setHomeLocationToAddress(newHomeAddress); + replaceDomainContent(url); details = "SuccessfulRequestToReplaceContent"; } else { details = "UserDeclinedToReplaceContent"; diff --git a/interface/src/Application.h b/interface/src/Application.h index d4041aa3be..2e73894b35 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -286,6 +286,8 @@ public: bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; } void saveNextPhysicsStats(QString filename); + void replaceDomainContent(const QString& url); + signals: void svoImportRequested(const QString& url); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f3d8ea2344..464de87fdb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -210,6 +210,7 @@ Menu::Menu() { auto avatarEntitiesBookmarks = DependencyManager::get(); avatarEntitiesBookmarks->setupMenus(this, avatarMenu); + // Display menu ---------------------------------- // FIXME - this is not yet matching Alan's spec because it doesn't have // menus for "2D"/"3D" - we need to add support for detecting the appropriate diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c25aaeeecd..50753724e8 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1479,7 +1479,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { }); saveAvatarUrl(); emit skeletonChanged(); - + emit skeletonModelURLChanged(); } void MyAvatar::removeAvatarEntities() { diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 10ddd4c110..dff441f840 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -49,6 +49,7 @@ Handler(balance) Handler(inventory) Handler(transferHfcToNode) Handler(transferHfcToUsername) +Handler(alreadyOwned) void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) { auto accountManager = DependencyManager::get(); @@ -336,3 +337,12 @@ void Ledger::transferHfcToUsername(const QString& hfc_key, const QString& userna auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_user", "transferHfcToUsernameSuccess", "transferHfcToUsernameFailure"); } + +void Ledger::alreadyOwned(const QString& marketplaceId) { + auto wallet = DependencyManager::get(); + QString endpoint = "already_owned"; + QJsonObject request; + request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys()); + request["marketplace_item_id"] = marketplaceId; + send(endpoint, "alreadyOwnedSuccess", "alreadyOwnedFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request); +} diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 1b56c893ab..ac9fe950d9 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -35,6 +35,7 @@ public: void certificateInfo(const QString& certificateId); void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage); void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage); + void alreadyOwned(const QString& marketplaceId); enum CertificateStatus { CERTIFICATE_STATUS_UNKNOWN = 0, @@ -55,6 +56,7 @@ signals: void certificateInfoResult(QJsonObject result); void transferHfcToNodeResult(QJsonObject result); void transferHfcToUsernameResult(QJsonObject result); + void alreadyOwnedResult(QJsonObject result); void updateCertificateStatus(const QString& certID, uint certStatus); @@ -79,6 +81,8 @@ public slots: void transferHfcToNodeFailure(QNetworkReply& reply); void transferHfcToUsernameSuccess(QNetworkReply& reply); void transferHfcToUsernameFailure(QNetworkReply& reply); + void alreadyOwnedSuccess(QNetworkReply& reply); + void alreadyOwnedFailure(QNetworkReply& reply); private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index c9caa393ce..36c1e422c5 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -15,6 +15,8 @@ #include "Ledger.h" #include "Wallet.h" #include +#include +#include QmlCommerce::QmlCommerce() { auto ledger = DependencyManager::get(); @@ -28,9 +30,11 @@ QmlCommerce::QmlCommerce() { connect(ledger.data(), &Ledger::accountResult, this, &QmlCommerce::accountResult); connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult); connect(ledger.data(), &Ledger::certificateInfoResult, this, &QmlCommerce::certificateInfoResult); + connect(ledger.data(), &Ledger::alreadyOwnedResult, this, &QmlCommerce::alreadyOwnedResult); connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus); connect(ledger.data(), &Ledger::transferHfcToNodeResult, this, &QmlCommerce::transferHfcToNodeResult); connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult); + connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult); auto accountManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { @@ -163,3 +167,19 @@ void QmlCommerce::transferHfcToUsername(const QString& username, const int& amou QString key = keys[0]; ledger->transferHfcToUsername(key, username, amount, optionalMessage); } + +void QmlCommerce::replaceContentSet(const QString& itemHref) { + qApp->replaceDomainContent(itemHref); + QJsonObject messageProperties = { + { "status", "SuccessfulRequestToReplaceContent" }, + { "content_set_url", itemHref } + }; + UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties); + + emit contentSetChanged(itemHref); +} + +void QmlCommerce::alreadyOwned(const QString& marketplaceId) { + auto ledger = DependencyManager::get(); + ledger->alreadyOwned(marketplaceId); +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index b261ee6de6..b621608190 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -42,12 +42,15 @@ signals: void historyResult(QJsonObject result); void accountResult(QJsonObject result); void certificateInfoResult(QJsonObject result); + void alreadyOwnedResult(QJsonObject result); void updateCertificateStatus(const QString& certID, uint certStatus); void transferHfcToNodeResult(QJsonObject result); void transferHfcToUsernameResult(QJsonObject result); + void contentSetChanged(const QString& contentSetHref); + protected: Q_INVOKABLE void getWalletStatus(); @@ -68,9 +71,13 @@ protected: Q_INVOKABLE void account(); Q_INVOKABLE void certificateInfo(const QString& certificateId); + Q_INVOKABLE void alreadyOwned(const QString& marketplaceId); Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage); Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage); + + + Q_INVOKABLE void replaceContentSet(const QString& itemHref); }; #endif // hifi_QmlCommerce_h diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index 012c37305d..cfa51697af 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -38,7 +38,7 @@ class AccountServicesScriptingInterface : public QObject { Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged) Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged) Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy NOTIFY findableByChanged) - Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL) + Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL CONSTANT) public: static AccountServicesScriptingInterface* getInstance(); diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index d9372978e8..d6dc2fa703 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -94,22 +94,6 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& return result; } -void MenuScriptingInterface::addActionGroup(const QString& groupName, const QStringList& actionList, - const QString& selected) { - static const char* slot = SLOT(menuItemTriggered()); - QMetaObject::invokeMethod(Menu::getInstance(), "addActionGroup", - Q_ARG(const QString&, groupName), - Q_ARG(const QStringList&, actionList), - Q_ARG(const QString&, selected), - Q_ARG(QObject*, this), - Q_ARG(const char*, slot)); -} - -void MenuScriptingInterface::removeActionGroup(const QString& groupName) { - QMetaObject::invokeMethod(Menu::getInstance(), "removeActionGroup", - Q_ARG(const QString&, groupName)); -} - bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { if (QThread::currentThread() == qApp->thread()) { return Menu::getInstance()->isOptionChecked(menuOption); @@ -147,19 +131,3 @@ void MenuScriptingInterface::setMenuEnabled(const QString& menuOption, bool isCh void MenuScriptingInterface::triggerOption(const QString& menuOption) { QMetaObject::invokeMethod(Menu::getInstance(), "triggerOption", Q_ARG(const QString&, menuOption)); } - -void MenuScriptingInterface::closeInfoView(const QString& path) { - QMetaObject::invokeMethod(Menu::getInstance(), "closeInfoView", Q_ARG(const QString&, path)); -} - -bool MenuScriptingInterface::isInfoViewVisible(const QString& path) { - if (QThread::currentThread() == qApp->thread()) { - return Menu::getInstance()->isInfoViewVisible(path); - } - - bool result; - BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isInfoViewVisible", - Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path)); - return result; -} - diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index 59cfc76d21..649c444eaf 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -163,13 +163,6 @@ public slots: */ bool menuItemExists(const QString& menuName, const QString& menuitem); - /** - * TODO: Not working; don't document until fixed. - */ - void addActionGroup(const QString& groupName, const QStringList& actionList, - const QString& selected = QString()); - void removeActionGroup(const QString& groupName); - /**jsdoc * Check whether a checkable menu item is checked. * @function Menu.isOptionChecked @@ -222,12 +215,6 @@ public slots: */ void setMenuEnabled(const QString& menuName, bool isEnabled); - /** - * TODO: Not used or useful; will not document until used. - */ - void closeInfoView(const QString& path); - bool isInfoViewVisible(const QString& path); - signals: /**jsdoc * Triggered when a menu item is clicked (or triggered by {@link Menu.triggerOption}). diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 529b8f796c..12b20566ed 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -32,20 +32,6 @@ static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation"; static const QString LAST_BROWSE_ASSETS_LOCATION_SETTING = "LastBrowseAssetsLocation"; -QScriptValue CustomPromptResultToScriptValue(QScriptEngine* engine, const CustomPromptResult& result) { - if (!result.value.isValid()) { - return QScriptValue::UndefinedValue; - } - - Q_ASSERT(result.value.userType() == qMetaTypeId()); - return engine->toScriptValue(result.value.toMap()); -} - -void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptResult& result) { - result.value = object.toVariant(); -} - - WindowScriptingInterface::WindowScriptingInterface() { const DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged); @@ -143,14 +129,6 @@ void WindowScriptingInterface::disconnectedFromDomain() { emit domainChanged(""); } -CustomPromptResult WindowScriptingInterface::customPrompt(const QVariant& config) { - CustomPromptResult result; - bool ok = false; - auto configMap = config.toMap(); - result.value = OffscreenUi::getCustomInfo(OffscreenUi::ICON_NONE, "", configMap, &ok); - return ok ? result : CustomPromptResult(); -} - QString fixupPathForMac(const QString& directory) { // On OS X `directory` does not work as expected unless a file is included in the path, so we append a bogus // filename if the directory is valid. diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 76decf4362..8d0c5c246b 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -22,17 +22,6 @@ #include -class CustomPromptResult { -public: - QVariant value; -}; - -Q_DECLARE_METATYPE(CustomPromptResult); - -QScriptValue CustomPromptResultToScriptValue(QScriptEngine* engine, const CustomPromptResult& result); -void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptResult& result); - - /**jsdoc * The Window API provides various facilities not covered elsewhere: window dimensions, window focus, normal or entity camera * view, clipboard, announcements, user connections, common dialog boxes, snapshots, file import, domain changes, domain @@ -142,15 +131,6 @@ public slots: */ void promptAsync(const QString& message = "", const QString& defaultText = ""); - /**jsdoc - * Prompt the user for input in a custom, modal dialog. - * @deprecated This function is deprecated and will soon be removed. - * @function Window.customPrompt - * @param {object} config - Configures the modal dialog. - * @returns {object} The user's response. - */ - CustomPromptResult customPrompt(const QVariant& config); - /**jsdoc * Prompt the user to choose a directory. Displays a modal dialog that navigates the directory tree. * @function Window.browseDir diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index b36b4f02df..23055cf246 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -42,8 +42,6 @@ private: int _domainStatusBorder; int _magnifierBorder; - ivec2 _previousBorderSize{ -1 }; - gpu::TexturePointer _uiTexture; gpu::TexturePointer _overlayDepthTexture; gpu::TexturePointer _overlayColorTexture; diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index f13f782482..29c4f592f5 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -125,13 +125,6 @@ Cube3DOverlay* Cube3DOverlay::createClone() const { void Cube3DOverlay::setProperties(const QVariantMap& properties) { Volume3DOverlay::setProperties(properties); - - auto borderSize = properties["borderSize"]; - - if (borderSize.isValid()) { - float value = borderSize.toFloat(); - setBorderSize(value); - } } /**jsdoc @@ -177,14 +170,8 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) { * parentID is an avatar skeleton. A value of 65535 means "no joint". * * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. - * - * @property {number} borderSize - Not used. */ QVariant Cube3DOverlay::getProperty(const QString& property) { - if (property == "borderSize") { - return _borderSize; - } - return Volume3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/Cube3DOverlay.h b/interface/src/ui/overlays/Cube3DOverlay.h index e7b58ad911..5d83cbb2c1 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.h +++ b/interface/src/ui/overlays/Cube3DOverlay.h @@ -29,10 +29,6 @@ public: virtual Cube3DOverlay* createClone() const override; - float getBorderSize() const { return _borderSize; } - - void setBorderSize(float value) { _borderSize = value; } - void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; @@ -40,7 +36,6 @@ protected: Transform evalRenderTransform() override; private: - float _borderSize; // edges on a cube std::array _geometryIds; }; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 6898b5ed2b..35274e4fbe 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -388,21 +388,6 @@ QString Overlays::getOverlayType(OverlayID overlayId) { return ""; } -QObject* Overlays::getOverlayObject(OverlayID id) { - if (QThread::currentThread() != thread()) { - QObject* result; - PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(OverlayID, id)); - return result; - } - - Overlay::Pointer thisOverlay = getOverlay(id); - if (thisOverlay) { - return qobject_cast(&(*thisOverlay)); - } - return nullptr; -} - OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) { if (!_enabled) { return UNKNOWN_OVERLAY_ID; diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 113ee92b80..3efe94c206 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -98,6 +98,11 @@ public: OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); } OverlayID addOverlay(const Overlay::Pointer& overlay); + RayToOverlayIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking, + const QVector& overlaysToInclude, + const QVector& overlaysToDiscard, + bool visibleOnly = false, bool collidableOnly = false); + bool mousePressEvent(QMouseEvent* event); bool mouseDoublePressEvent(QMouseEvent* event); bool mouseReleaseEvent(QMouseEvent* event); @@ -227,15 +232,6 @@ public slots: */ QString getOverlayType(OverlayID overlayId); - /**jsdoc - * Get the overlay script object. - * @function Overlays.getOverlayObject - * @deprecated This function is deprecated and will soon be removed. - * @param {Uuid} overlayID - The ID of the overlay to get the script object of. - * @returns {object} The script object for the overlay if found. - */ - QObject* getOverlayObject(OverlayID id); - /**jsdoc * Get the ID of the 2D overlay at a particular point on the screen or HUD. * @function Overlays.getOverlayAtPoint @@ -356,28 +352,6 @@ public slots: bool visibleOnly = false, bool collidableOnly = false); - // TODO: Apart from the name, this function signature on JavaScript is identical to that of findRayIntersection() and should - // probably be removed from the JavaScript API so as not to cause confusion. - /**jsdoc - * Find the closest 3D overlay intersected by a {@link PickRay}. - * @function Overlays.findRayIntersectionVector - * @deprecated Use {@link Overlays.findRayIntersection} instead; it has identical parameters and results. - * @param {PickRay} pickRay - The PickRay to use for finding overlays. - * @param {boolean} [precisionPicking=false] - Unused; exists to match Entity API. - * @param {Array.} [overlayIDsToInclude=[]] - Whitelist for intersection test. If empty then the result isn't limited - * to overlays in the list. - * @param {Array.} [overlayIDsToExclude=[]] - Blacklist for intersection test. If empty then the result doesn't - * exclude overlays in the list. - * @param {boolean} [visibleOnly=false] - Unused; exists to match Entity API. - * @param {boolean} [collidableOnly=false] - Unused; exists to match Entity API. - * @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by pickRay, taking - * into account overlayIDsToInclude and overlayIDsToExclude if they're not empty. - */ - RayToOverlayIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking, - const QVector& overlaysToInclude, - const QVector& overlaysToDiscard, - bool visibleOnly = false, bool collidableOnly = false); - /**jsdoc * Return a list of 3D overlays with bounding boxes that touch a search sphere. * @function Overlays.findOverlays diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index 97342a80ab..64acdcccdb 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -99,13 +99,6 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) { } } } - - auto borderSize = properties["borderSize"]; - - if (borderSize.isValid()) { - float value = borderSize.toFloat(); - setBorderSize(value); - } } /**jsdoc @@ -153,13 +146,8 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) { * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. * * @property {Shape} shape=Hexagon - The geometrical shape of the overlay. - * @property {number} borderSize - Not used. */ QVariant Shape3DOverlay::getProperty(const QString& property) { - if (property == "borderSize") { - return _borderSize; - } - if (property == "shape") { return shapeStrings[_shape]; } diff --git a/interface/src/ui/overlays/Shape3DOverlay.h b/interface/src/ui/overlays/Shape3DOverlay.h index 7fc95ec981..0196c707d7 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.h +++ b/interface/src/ui/overlays/Shape3DOverlay.h @@ -30,10 +30,6 @@ public: virtual Shape3DOverlay* createClone() const override; - float getBorderSize() const { return _borderSize; } - - void setBorderSize(float value) { _borderSize = value; } - void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; @@ -41,7 +37,6 @@ protected: Transform evalRenderTransform() override; private: - float _borderSize; GeometryCache::Shape _shape { GeometryCache::Hexagon }; }; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 01b7dfb0de..d6791ab0b8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -434,9 +434,13 @@ void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, flo bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const { bool success { false }; - if (QThread::currentThread() == thread()) { + glm::vec3 originalPosition = position; + bool onOwnerThread = (QThread::currentThread() == thread()); + glm::vec3 poseSetTrans; + if (onOwnerThread) { if (isIndexValid(jointIndex)) { - position = (rotation * _internalPoseSet._absolutePoses[jointIndex].trans()) + translation; + poseSetTrans = _internalPoseSet._absolutePoses[jointIndex].trans(); + position = (rotation * poseSetTrans) + translation; success = true; } else { success = false; @@ -444,7 +448,8 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm: } else { QReadLocker readLock(&_externalPoseSetLock); if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._absolutePoses.size()) { - position = (rotation * _externalPoseSet._absolutePoses[jointIndex].trans()) + translation; + poseSetTrans = _externalPoseSet._absolutePoses[jointIndex].trans(); + position = (rotation * poseSetTrans) + translation; success = true; } else { success = false; @@ -452,7 +457,14 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm: } if (isNaN(position)) { - qCWarning(animation) << "Rig::getJointPositionInWorldFrame produces NaN"; + qCWarning(animation) << "Rig::getJointPositionInWorldFrame produced NaN." + << " is owner thread = " << onOwnerThread + << " position = " << originalPosition + << " translation = " << translation + << " rotation = " << rotation + << " poseSetTrans = " << poseSetTrans + << " success = " << success + << " jointIndex = " << jointIndex; success = false; position = glm::vec3(0.0f); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 033756bfd2..e927120b07 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -373,7 +373,7 @@ class AvatarData : public QObject, public SpatiallyNestable { // The result is unique among all avatars present at the time. Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName NOTIFY sessionDisplayNameChanged) Q_PROPERTY(bool lookAtSnappingEnabled MEMBER _lookAtSnappingEnabled NOTIFY lookAtSnappingChanged) - Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript) + Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript NOTIFY skeletonModelURLChanged) Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) Q_PROPERTY(QStringList jointNames READ getJointNames) @@ -702,6 +702,7 @@ public: signals: void displayNameChanged(); void sessionDisplayNameChanged(); + void skeletonModelURLChanged(); void lookAtSnappingChanged(bool enabled); void sessionUUIDChanged(); diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index 1fd001e536..8491e5368b 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -16,6 +16,7 @@ ScriptAvatarData::ScriptAvatarData(AvatarSharedPointer avatarData) : { QObject::connect(avatarData.get(), &AvatarData::displayNameChanged, this, &ScriptAvatarData::displayNameChanged); QObject::connect(avatarData.get(), &AvatarData::sessionDisplayNameChanged, this, &ScriptAvatarData::sessionDisplayNameChanged); + QObject::connect(avatarData.get(), &AvatarData::skeletonModelURLChanged, this, &ScriptAvatarData::skeletonModelURLChanged); QObject::connect(avatarData.get(), &AvatarData::lookAtSnappingChanged, this, &ScriptAvatarData::lookAtSnappingChanged); } diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 68074b838d..13713ff15f 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -51,7 +51,7 @@ class ScriptAvatarData : public QObject { // // ATTACHMENT AND JOINT PROPERTIES // - Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript) + Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript NOTIFY skeletonModelURLChanged) Q_PROPERTY(QVector attachmentData READ getAttachmentData) Q_PROPERTY(QStringList jointNames READ getJointNames) @@ -132,6 +132,7 @@ public: signals: void displayNameChanged(); void sessionDisplayNameChanged(); + void skeletonModelURLChanged(); void lookAtSnappingChanged(bool enabled); public slots: diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 8bdf0252cf..2b44b47128 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -101,6 +101,11 @@ bool EntityScriptingInterface::canWriteAssets() { return nodeList->getThisNodeCanWriteAssets(); } +bool EntityScriptingInterface::canReplaceContent() { + auto nodeList = DependencyManager::get(); + return nodeList->getThisNodeCanReplaceContent(); +} + void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) { if (_entityTree) { disconnect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 919d0e3489..cdb784ffa8 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -144,6 +144,12 @@ public slots: */ Q_INVOKABLE bool canWriteAssets(); + /**jsdoc + * @function Entities.canReplaceContent + * @return {bool} `true` if the DomainServer will allow this Node/Avatar to replace the domain's content set + */ + Q_INVOKABLE bool canReplaceContent(); + /**jsdoc * Add a new entity with the specified properties. If `clientOnly` is true, the entity will * not be sent to the server and will only be visible/accessible on the local client. diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index 87fc8a3025..a84f3feb4d 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -331,9 +331,9 @@ void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent, qmlComponent->deleteLater(); onItemCreated(qmlContext, newItem); - connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant))); if (!rootCreated) { + connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant))); onRootCreated(); emit rootItemCreated(newItem); // Call this callback after rootitem is set, otherwise VrMenu wont work diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index e45a35a0fc..4309789b03 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -389,19 +389,6 @@ QString OffscreenUi::getItem(const Icon icon, const QString& title, const QStrin return result.toString(); } -QVariant OffscreenUi::getCustomInfo(const Icon icon, const QString& title, const QVariantMap& config, bool* ok) { - if (ok) { - *ok = false; - } - - QVariant result = DependencyManager::get()->customInputDialog(icon, title, config); - if (ok && result.isValid()) { - *ok = true; - } - - return result; -} - ModalDialogListener* OffscreenUi::getTextAsync(const Icon icon, const QString& title, const QString& label, const QString& text) { return DependencyManager::get()->inputDialogAsync(icon, title, label, text); } @@ -423,10 +410,6 @@ ModalDialogListener* OffscreenUi::getItemAsync(const Icon icon, const QString& t return inputDialogListener; } -ModalDialogListener* OffscreenUi::getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config) { - return DependencyManager::get()->customInputDialogAsync(icon, title, config); -} - QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current) { if (QThread::currentThread() != thread()) { QVariant result; diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index e507333840..cb8ee29068 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -230,10 +230,8 @@ public: static QString getText(const Icon icon, const QString & title, const QString & label, const QString & text = QString(), bool * ok = 0); static QString getItem(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0); - static QVariant getCustomInfo(const Icon icon, const QString& title, const QVariantMap& config, bool* ok = 0); static ModalDialogListener* getTextAsync(const Icon icon, const QString & title, const QString & label, const QString & text = QString()); static ModalDialogListener* getItemAsync(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true); - static ModalDialogListener* getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config); unsigned int getMenuUserDataId() const; QList &getModalDialogListeners(); diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp index b600fc7b29..16af862324 100644 --- a/libraries/ui/src/ui/Menu.cpp +++ b/libraries/ui/src/ui/Menu.cpp @@ -268,16 +268,6 @@ bool Menu::isOptionChecked(const QString& menuOption) const { return false; } -void Menu::closeInfoView(const QString& path) { - auto offscreenUi = DependencyManager::get(); - offscreenUi->hide(path); -} - -bool Menu::isInfoViewVisible(const QString& path) { - auto offscreenUi = DependencyManager::get(); - return offscreenUi->isVisible(path); -} - void Menu::triggerOption(const QString& menuOption) { QAction* action = _actionHash.value(menuOption); if (action) { @@ -538,24 +528,6 @@ void Menu::setGroupingIsVisible(const QString& grouping, bool isVisible) { QMenuBar::repaint(); } -void Menu::addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected, QObject* receiver, const char* slot) { - auto menu = addMenu(groupName); - - QActionGroup* actionGroup = new QActionGroup(menu); - actionGroup->setExclusive(true); - - for (auto action : actionList) { - auto item = addCheckableActionToQMenuAndActionHash(menu, action, 0, action == selected, receiver, slot); - actionGroup->addAction(item); - } - - QMenuBar::repaint(); -} - -void Menu::removeActionGroup(const QString& groupName) { - removeMenu(groupName); -} - MenuWrapper::MenuWrapper(ui::Menu& rootMenu, QMenu* menu) : _rootMenu(rootMenu), _realMenu(menu) { auto offscreenUi = DependencyManager::get(); offscreenUi->addMenuInitializer([=](VrMenu* vrMenu) { diff --git a/libraries/ui/src/ui/Menu.h b/libraries/ui/src/ui/Menu.h index 25f8f74063..2977a5330a 100644 --- a/libraries/ui/src/ui/Menu.h +++ b/libraries/ui/src/ui/Menu.h @@ -110,9 +110,6 @@ public slots: void removeSeparator(const QString& menuName, const QString& separatorName); void removeMenuItem(const QString& menuName, const QString& menuitem); bool menuItemExists(const QString& menuName, const QString& menuitem); - void addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected = QString(), - QObject* receiver = nullptr, const char* slot = nullptr); - void removeActionGroup(const QString& groupName); bool isOptionChecked(const QString& menuOption) const; void setIsOptionChecked(const QString& menuOption, bool isChecked); @@ -125,9 +122,6 @@ public slots: void toggleDeveloperMenus(); void toggleAdvancedMenus(); - bool isInfoViewVisible(const QString& path); - void closeInfoView(const QString& path); - void triggerOption(const QString& menuOption); static bool isSomeSubmenuShown() { return _isSomeSubmenuShown; } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index ea34f3de76..749a60a578 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -305,7 +305,6 @@ void OffscreenQmlSurface::onItemCreated(QQmlContext* qmlContext, QQuickItem* new qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(eventBridge, qmlContext)); } - connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant))); } void OffscreenQmlSurface::onRootCreated() { diff --git a/scripts/developer/debugging/queryAACubeInspector.js b/scripts/developer/debugging/queryAACubeInspector.js index 2d585ffce4..d8a87c3cf5 100644 --- a/scripts/developer/debugging/queryAACubeInspector.js +++ b/scripts/developer/debugging/queryAACubeInspector.js @@ -40,7 +40,6 @@ function updateOverlay(entityID, queryAACube) { blue: 255 }, alpha: 1, - // borderSize: ..., solid: false }); } diff --git a/scripts/system/audio.js b/scripts/system/audio.js index a93177ca38..ee82c0c6ea 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -42,8 +42,9 @@ function onClicked() { // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); } else { - var entity = HMD.tabletID; - Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + if (HMD.tabletID) { + Entities.editEntity(HMD.tabletID, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + } tablet.loadQMLSource(AUDIO_QML_SOURCE); } } diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 16f1d086b7..345ab33c0d 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -146,7 +146,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.setIgnorePointerItems = function() { - if (HMD.tabletID !== this.tabletID) { + if (HMD.tabletID && HMD.tabletID !== this.tabletID) { this.tabletID = HMD.tabletID; Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist); Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist); @@ -479,7 +479,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; } function mouseReleaseOnOverlay(overlayID, event) { - if (overlayID === HMD.homeButtonID && event.button === "Primary") { + if (HMD.homeButtonID && overlayID === HMD.homeButtonID && event.button === "Primary") { Messages.sendLocalMessage("home", overlayID); } } diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 763258573d..202290f2df 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -32,7 +32,7 @@ Script.include("/~/system/libraries/utils.js"); this.nearTablet = function(overlays) { for (var i = 0; i < overlays.length; i++) { - if (overlays[i] === HMD.tabletID) { + if (HMD.tabletID && overlays[i] === HMD.tabletID) { return true; } } @@ -44,7 +44,8 @@ Script.include("/~/system/libraries/utils.js"); }; this.pointingAtTablet = function(objectID) { - return objectID === HMD.tabletScreenID || objectID === HMD.homeButtonID; + return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) + || (HMD.homeButtonID && objectID === HMD.homeButtonID); }; this.sendPickData = function(controllerData) { @@ -106,7 +107,7 @@ Script.include("/~/system/libraries/utils.js"); if (nearOverlay) { var nearOverlayReady = nearOverlay.isReady(controllerData); - if (nearOverlayReady.active && nearOverlay.grabbedThingID === HMD.tabletID) { + if (nearOverlayReady.active && HMD.tabletID && nearOverlay.grabbedThingID === HMD.tabletID) { return this.exitModule(); } } diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index 38eca65dd3..7b78d5e1c4 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -31,7 +31,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); ); this.pointingAtTablet = function (objectID) { - return objectID === HMD.tabletScreenID || objectID === HMD.homeButtonID; + return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) + || (HMD.homeButtonID && objectID === HMD.homeButtonID); }; this.isReady = function (controllerData) { @@ -76,7 +77,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); : "LeftNearParentingGrabOverlay"); if (nearOverlay) { var nearOverlayReady = nearOverlay.isReady(controllerData); - if (nearOverlayReady.active && nearOverlay.grabbedThingID === HMD.tabletID) { + if (nearOverlayReady.active && HMD.tabletID && nearOverlay.grabbedThingID === HMD.tabletID) { return makeRunningValues(false, [], []); } } diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index fa0fe31de2..0f876816b3 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -117,7 +117,7 @@ Script.include("/~/system/libraries/utils.js"); Overlays.editOverlay(this.grabbedThingID, reparentProps); // resizeTablet to counter adjust offsets to account for change of scale from sensorToWorldMatrix - if (this.grabbedThingID === HMD.tabletID) { + if (HMD.tabletID && this.grabbedThingID === HMD.tabletID) { resizeTablet(getTabletWidthFromSettings(), reparentProps.parentJointIndex); } @@ -143,7 +143,7 @@ Script.include("/~/system/libraries/utils.js"); }); // resizeTablet to counter adjust offsets to account for change of scale from sensorToWorldMatrix - if (this.grabbedThingID === HMD.tabletID) { + if (HMD.tabletID && this.grabbedThingID === HMD.tabletID) { resizeTablet(getTabletWidthFromSettings(), this.previousParentJointIndex[this.grabbedThingID]); } } diff --git a/scripts/system/controllers/controllerModules/stylusInput.js b/scripts/system/controllers/controllerModules/stylusInput.js index aa65135289..a512fd89db 100644 --- a/scripts/system/controllers/controllerModules/stylusInput.js +++ b/scripts/system/controllers/controllerModules/stylusInput.js @@ -20,7 +20,7 @@ Script.include("/~/system/libraries/controllers.js"); var stylusTargetIDs = []; for (var index = 0; index < stylusTargets.length; index++) { var stylusTarget = stylusTargets[index]; - if (stylusTarget.distance <= maxNormalDistance && stylusTarget.id !== HMD.tabletID) { + if (stylusTarget.distance <= maxNormalDistance && !(HMD.tabletID && stylusTarget.id === HMD.tabletID)) { stylusTargetIDs.push(stylusTarget.id); } } @@ -96,7 +96,7 @@ Script.include("/~/system/libraries/controllers.js"); var i, stylusTarget; for (i = 0; i < candidateOverlays.length; i++) { - if (candidateOverlays[i] !== HMD.tabletID && + if (!(HMD.tabletID && candidateOverlays[i] === HMD.tabletID) && Overlays.getProperty(candidateOverlays[i], "visible")) { stylusTarget = getOverlayDistance(controllerPosition, candidateOverlays[i]); if (stylusTarget) { diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index a51cea67f8..b62cb3dd90 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -263,7 +263,10 @@ function Grabber() { filter: Picks.PICK_OVERLAYS, enabled: true }); - RayPick.setIncludeItems(this.mouseRayOverlays, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]); + var tabletItems = getMainTabletIDs(); + if (tabletItems.length > 0) { + RayPick.setIncludeItems(this.mouseRayOverlays, tabletItems); + } var renderStates = [{name: "grabbed", end: beacon}]; this.mouseRayEntities = Pointers.createPointer(PickType.Ray, { joint: "Mouse", diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 0167b55810..2853355c5a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -31,6 +31,7 @@ Script.include([ "libraries/entityCameraTool.js", "libraries/gridTool.js", "libraries/entityList.js", + "libraries/utils.js", "particle_explorer/particleExplorerTool.js", "libraries/entityIconOverlayManager.js" ]); @@ -775,8 +776,7 @@ function findClickedEntity(event) { } var pickRay = Camera.computePickRay(event.x, event.y); - - var overlayResult = Overlays.findRayIntersection(pickRay, true, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]); + var overlayResult = Overlays.findRayIntersection(pickRay, true, getMainTabletIDs()); if (overlayResult.intersects) { return null; } @@ -964,7 +964,7 @@ function mouseReleaseEvent(event) { function wasTabletClicked(event) { var rayPick = Camera.computePickRay(event.x, event.y); - var result = Overlays.findRayIntersection(rayPick, true, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]); + var result = Overlays.findRayIntersection(rayPick, true, getMainTabletIDs()); return result.intersects; } @@ -987,7 +987,7 @@ function mouseClickEvent(event) { toolBar.setActive(true); var pickRay = result.pickRay; var foundEntity = result.entityID; - if (foundEntity === HMD.tabletID) { + if (HMD.tabletID && foundEntity === HMD.tabletID) { return; } properties = Entities.getEntityProperties(foundEntity); diff --git a/scripts/system/help.js b/scripts/system/help.js index 9ab7fa3fb3..02d6969718 100644 --- a/scripts/system/help.js +++ b/scripts/system/help.js @@ -30,9 +30,8 @@ if (onHelpScreen) { tablet.gotoHomeScreen(); } else { - var tabletEntity = HMD.tabletID; - if (tabletEntity) { - Entities.editEntity(tabletEntity, {textures: JSON.stringify({"tex.close" : HOME_BUTTON_TEXTURE})}); + if (HMD.tabletID) { + Entities.editEntity(HMD.tabletID, {textures: JSON.stringify({"tex.close" : HOME_BUTTON_TEXTURE})}); } Menu.triggerOption('Help...'); onHelpScreen = true; @@ -47,22 +46,12 @@ button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); - var POLL_RATE = 500; - var interval = Script.setInterval(function () { - var visible = Menu.isInfoViewVisible('InfoView_html/help.html'); - if (visible !== enabled) { - enabled = visible; - button.editProperties({isActive: enabled}); - } - }, POLL_RATE); - Script.scriptEnding.connect(function () { if (onHelpScreen) { tablet.gotoHomeScreen(); } button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); - Script.clearInterval(interval); if (tablet) { tablet.removeButton(button); } diff --git a/scripts/system/html/css/colpick.css b/scripts/system/html/css/colpick.css index 98417a5e9a..fc50c4b3fb 100644 --- a/scripts/system/html/css/colpick.css +++ b/scripts/system/html/css/colpick.css @@ -279,7 +279,7 @@ colpick Color Picker / colpick.com } /*full layout with no submit button*/ -.colpick_full_ns .colpick_submit, .colpick_full_ns .colpick_current_color{ +.colpick_full_ns .colpick_submit { display:none; } .colpick_full_ns .colpick_new_color { @@ -320,11 +320,11 @@ colpick Color Picker / colpick.com } /*rgbhex layout, no submit button*/ -.colpick_rgbhex_ns .colpick_submit, .colpick_rgbhex_ns .colpick_current_color{ +.colpick_rgbhex_ns .colpick_submit { display:none; } .colpick_rgbhex_ns .colpick_new_color{ - width:68px; + width:34px; border: 1px solid #8f8f8f; } .colpick_rgbhex_ns .colpick_rgb_r { @@ -379,7 +379,7 @@ colpick Color Picker / colpick.com } /*hex layout, no submit button*/ -.colpick_hex_ns .colpick_submit, .colpick_hex_ns .colpick_current_color { +.colpick_hex_ns .colpick_submit { display:none; } .colpick_hex_ns .colpick_hex_field { diff --git a/scripts/system/html/js/colpick.js b/scripts/system/html/js/colpick.js index f808262e9e..199c624bc5 100644 --- a/scripts/system/html/js/colpick.js +++ b/scripts/system/html/js/colpick.js @@ -1,14 +1,41 @@ /* colpick Color Picker -Copyright 2013 Jose Vargas. Licensed under GPL license. Based on Stefan Petre's Color Picker www.eyecon.ro, dual licensed under the MIT and GPL licenses +Copyright 2013 Jose Vargas. Licensed under GPL license. Based on Stefan Petre's Color Picker www.eyecon.ro, dual licensed +under the MIT and GPL licenses For usage and examples: colpick.com/plugin */ +/* global console, document, Element, EventBridge, jQuery, navigator, window, _ $ */ + (function ($) { var colpick = function () { var - tpl = '
#
R
G
B
H
S
B
', + tpl = '
' + + '
' + + '
' + + '
' + + '
' + + '
#
' + + '
' + + '
R
' + + '
' + + '
' + + '
G
' + + '
' + + '
B
' + + '
' + + '
' + + '
H
' + + '
' + + '
' + + '
S
' + + '
' + + '
' + + '
B
' + + '
' + + '
' + + '
', defaults = { showEvent: 'click', onShow: function () {}, @@ -25,15 +52,15 @@ For usage and examples: colpick.com/plugin submitText: 'OK', height: 156 }, - //Fill the inputs of the plugin - fillRGBFields = function (hsb, cal) { + // Fill the inputs of the plugin + fillRGBFields = function (hsb, cal) { var rgb = hsbToRgb(hsb); $(cal).data('colpick').fields .eq(1).val(rgb.r).end() .eq(2).val(rgb.g).end() .eq(3).val(rgb.b).end(); }, - fillHSBFields = function (hsb, cal) { + fillHSBFields = function (hsb, cal) { $(cal).data('colpick').fields .eq(4).val(Math.round(hsb.h)).end() .eq(5).val(Math.round(hsb.s)).end() @@ -42,7 +69,7 @@ For usage and examples: colpick.com/plugin fillHexFields = function (hsb, cal) { $(cal).data('colpick').fields.eq(0).val(hsbToHex(hsb)); }, - //Set the round selector position + // Set the round selector position setSelector = function (hsb, cal) { $(cal).data('colpick').selector.css('backgroundColor', '#' + hsbToHex({h: hsb.h, s: 100, b: 100})); $(cal).data('colpick').selectorIndic.css({ @@ -50,18 +77,19 @@ For usage and examples: colpick.com/plugin top: parseInt($(cal).data('colpick').height * (100-hsb.b)/100, 10) }); }, - //Set the hue selector position + // Set the hue selector position setHue = function (hsb, cal) { - $(cal).data('colpick').hue.css('top', parseInt($(cal).data('colpick').height - $(cal).data('colpick').height * hsb.h/360, 10)); + $(cal).data('colpick').hue.css('top', + parseInt($(cal).data('colpick').height - $(cal).data('colpick').height * hsb.h / 360, 10)); }, - //Set current and new colors + // Set current and new colors setCurrentColor = function (hsb, cal) { $(cal).data('colpick').currentColor.css('backgroundColor', '#' + hsbToHex(hsb)); }, setNewColor = function (hsb, cal) { $(cal).data('colpick').newColor.css('backgroundColor', '#' + hsbToHex(hsb)); }, - //Called when the new color is changed + // Called when the new color is changed change = function (ev) { var cal = $(this).parent().parent(), col; if (this.parentNode.className.indexOf('_hex') > 0) { @@ -91,9 +119,10 @@ For usage and examples: colpick.com/plugin setSelector(col, cal.get(0)); setHue(col, cal.get(0)); setNewColor(col, cal.get(0)); - cal.data('colpick').onChange.apply(cal.parent(), [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 0]); + cal.data('colpick').onChange.apply(cal.parent(), + [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 0]); }, - //Change style on blur and on focus of inputs + // Change style on blur and on focus of inputs blur = function (ev) { $(this).parent().removeClass('colpick_focus'); }, @@ -101,13 +130,14 @@ For usage and examples: colpick.com/plugin $(this).parent().parent().data('colpick').fields.parent().removeClass('colpick_focus'); $(this).parent().addClass('colpick_focus'); }, - //Increment/decrement arrows functions + // Increment/decrement arrows functions downIncrement = function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; var field = $(this).parent().find('input').focus(); var current = { el: $(this).parent().addClass('colpick_slider'), - max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), + max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : + (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), y: ev.pageY, field: field, val: parseInt(field.val(), 10), @@ -130,7 +160,7 @@ For usage and examples: colpick.com/plugin $(document).off('mousemove', moveIncrement); return false; }, - //Hue slider functions + // Hue slider functions downHue = function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; var current = { @@ -140,20 +170,23 @@ For usage and examples: colpick.com/plugin $(document).on('mouseup touchend',current,upHue); $(document).on('mousemove touchmove',current,moveHue); - var pageY = ((ev.type == 'touchstart') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY ); + var pageY = ((ev.type === 'touchstart') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY ); change.apply( current.cal.data('colpick') - .fields.eq(4).val(parseInt(360*(current.cal.data('colpick').height - (pageY - current.y))/current.cal.data('colpick').height, 10)) + .fields.eq(4).val(parseInt(360 * (current.cal.data('colpick').height - + (pageY - current.y)) / current.cal.data('colpick').height, 10)) .get(0), [current.cal.data('colpick').livePreview] ); return false; }, moveHue = function (ev) { - var pageY = ((ev.type == 'touchmove') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY ); + var pageY = ((ev.type === 'touchmove') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY ); change.apply( ev.data.cal.data('colpick') - .fields.eq(4).val(parseInt(360*(ev.data.cal.data('colpick').height - Math.max(0,Math.min(ev.data.cal.data('colpick').height,(pageY - ev.data.y))))/ev.data.cal.data('colpick').height, 10)) + .fields.eq(4).val(parseInt(360 * (ev.data.cal.data('colpick').height - + Math.max(0, Math.min(ev.data.cal.data('colpick').height, (pageY - ev.data.y)))) / + ev.data.cal.data('colpick').height, 10)) .get(0), [ev.data.preview] ); @@ -166,7 +199,7 @@ For usage and examples: colpick.com/plugin $(document).off('mousemove touchmove',moveHue); return false; }, - //Color selector functions + // Color selector functions downSelector = function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; var current = { @@ -178,8 +211,8 @@ For usage and examples: colpick.com/plugin $(document).on('mouseup touchend',current,upSelector); $(document).on('mousemove touchmove',current,moveSelector); - var payeX,pageY; - if(ev.type == 'touchstart') { + var pageX,pageY; + if (ev.type === 'touchstart') { pageX = ev.originalEvent.changedTouches[0].pageX, pageY = ev.originalEvent.changedTouches[0].pageY; } else { @@ -189,16 +222,17 @@ For usage and examples: colpick.com/plugin change.apply( current.cal.data('colpick').fields - .eq(6).val(parseInt(100*(current.cal.data('colpick').height - (pageY - current.pos.top))/current.cal.data('colpick').height, 10)).end() - .eq(5).val(parseInt(100*(pageX - current.pos.left)/current.cal.data('colpick').height, 10)) - .get(0), + .eq(6).val(parseInt(100 * (current.cal.data('colpick').height - (pageY - current.pos.top)) / + current.cal.data('colpick').height, 10)).end() + .eq(5).val(parseInt(100*(pageX - current.pos.left)/current.cal.data('colpick').height, 10)) + .get(0), [current.preview] ); return false; }, moveSelector = function (ev) { - var payeX,pageY; - if(ev.type == 'touchmove') { + var pageX,pageY; + if (ev.type === 'touchmove') { pageX = ev.originalEvent.changedTouches[0].pageX, pageY = ev.originalEvent.changedTouches[0].pageY; } else { @@ -208,9 +242,12 @@ For usage and examples: colpick.com/plugin change.apply( ev.data.cal.data('colpick').fields - .eq(6).val(parseInt(100*(ev.data.cal.data('colpick').height - Math.max(0,Math.min(ev.data.cal.data('colpick').height,(pageY - ev.data.pos.top))))/ev.data.cal.data('colpick').height, 10)).end() - .eq(5).val(parseInt(100*(Math.max(0,Math.min(ev.data.cal.data('colpick').height,(pageX - ev.data.pos.left))))/ev.data.cal.data('colpick').height, 10)) - .get(0), + .eq(6).val(parseInt(100 * (ev.data.cal.data('colpick').height - + Math.max(0, Math.min(ev.data.cal.data('colpick').height, (pageY - ev.data.pos.top)))) / + ev.data.cal.data('colpick').height, 10)).end() + .eq(5).val(parseInt(100 * (Math.max(0, Math.min(ev.data.cal.data('colpick').height, + (pageX - ev.data.pos.left)))) / ev.data.cal.data('colpick').height, 10)) + .get(0), [ev.data.preview] ); return false; @@ -222,7 +259,7 @@ For usage and examples: colpick.com/plugin $(document).off('mousemove touchmove',moveSelector); return false; }, - //Submit button + // Submit button clickSubmit = function (ev) { var cal = $(this).parent(); var col = cal.data('colpick').color; @@ -230,7 +267,7 @@ For usage and examples: colpick.com/plugin setCurrentColor(col, cal.get(0)); cal.data('colpick').onSubmit(col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el); }, - //Show/hide the color picker + // Show/hide the color picker show = function (ev) { // Prevent the trigger of any direct parent ev.stopPropagation(); @@ -245,27 +282,29 @@ For usage and examples: colpick.com/plugin left -= calW; } cal.css({left: left + 'px', top: top + 'px'}); - if (cal.data('colpick').onShow.apply(this, [cal.get(0)]) != false) { + if (cal.data('colpick').onShow.apply(this, [cal.get(0)]) !== false) { cal.show(); } - //Hide when user clicks outside + // Hide when user clicks outside $('html').mousedown({cal:cal}, hide); - cal.mousedown(function(ev){ev.stopPropagation();}) + cal.mousedown(function(ev){ + ev.stopPropagation(); + }); }, hide = function (ev) { - if (ev.data.cal.data('colpick').onHide.apply(this, [ev.data.cal.get(0)]) != false) { + if (ev.data.cal.data('colpick').onHide.apply(this, [ev.data.cal.get(0)]) !== false) { ev.data.cal.hide(); } $('html').off('mousedown', hide); }, getViewport = function () { - var m = document.compatMode == 'CSS1Compat'; + var m = document.compatMode === 'CSS1Compat'; return { l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth) }; }, - //Fix the values if the user enters a negative or high value + // Fix the values if the user enters a negative or high value fixHSB = function (hsb) { return { h: Math.min(360, Math.max(0, hsb.h)), @@ -302,70 +341,85 @@ For usage and examples: colpick.com/plugin setSelector(col, cal.get(0)); setHue(col, cal.get(0)); setNewColor(col, cal.get(0)); + // If the user triggered this behavior, then any prior color change should be negated. + cal.data('colpick').onChange.apply(cal.parent(), [col, hsbToHex(col), + hsbToRgb(col), cal.data('colpick').el, 0]); }; return { init: function (opt) { opt = $.extend({}, defaults, opt||{}); - //Set color - if (typeof opt.color == 'string') { + // Set color + if (typeof opt.color === 'string') { opt.color = hexToHsb(opt.color); - } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) { + } else if (opt.color.r !== undefined && opt.color.g !== undefined && opt.color.b !== undefined) { opt.color = rgbToHsb(opt.color); - } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) { + } else if (opt.color.h !== undefined && opt.color.s !== undefined && opt.color.b !== undefined) { opt.color = fixHSB(opt.color); } else { return this; } - //For each selected DOM element + // For each selected DOM element return this.each(function () { - //If the element does not have an ID + // If the element does not have an ID if (!$(this).data('colpickId')) { var options = $.extend({}, opt); options.origColor = opt.color; - //Generate and assign a random ID + // Generate and assign a random ID var id = 'collorpicker_' + parseInt(Math.random() * 1000); $(this).data('colpickId', id); - //Set the tpl's ID and get the HTML + // Set the tpl's ID and get the HTML var cal = $(tpl).attr('id', id); - //Add class according to layout + // Add class according to layout cal.addClass('colpick_'+options.layout+(options.submit?'':' colpick_'+options.layout+'_ns')); - //Add class if the color scheme is not default - if(options.colorScheme != 'light') { + // Add class if the color scheme is not default + if (options.colorScheme !== 'light') { cal.addClass('colpick_'+options.colorScheme); } - //Setup submit button + // Setup submit button cal.find('div.colpick_submit').html(options.submitText).click(clickSubmit); - //Setup input fields + // Setup input fields options.fields = cal.find('input').change(change).blur(blur).focus(focus); - cal.find('div.colpick_field_arrs').mousedown(downIncrement).end().find('div.colpick_current_color').click(restoreOriginal); - //Setup hue selector + cal.find('div.colpick_field_arrs').mousedown(downIncrement); + cal.find('div.colpick_current_color').click(restoreOriginal); + // Setup hue selector options.selector = cal.find('div.colpick_color').on('mousedown touchstart',downSelector); options.selectorIndic = options.selector.find('div.colpick_selector_outer'); - //Store parts of the plugin + // Store parts of the plugin options.el = this; options.hue = cal.find('div.colpick_hue_arrs'); - huebar = options.hue.parent(); - //Paint the hue bar + var huebar = options.hue.parent(); + // Paint the hue bar var UA = navigator.userAgent.toLowerCase(); var isIE = navigator.appName === 'Microsoft Internet Explorer'; - var IEver = isIE ? parseFloat( UA.match( /msie ([0-9]{1,}[\.0-9]{0,})/ )[1] ) : 0; + var IEver = isIE ? parseFloat( UA.match( /msie ([0-9]{1,}[.0-9]{0,})/ )[1] ) : 0; var ngIE = ( isIE && IEver < 10 ); - var stops = ['#ff0000','#ff0080','#ff00ff','#8000ff','#0000ff','#0080ff','#00ffff','#00ff80','#00ff00','#80ff00','#ffff00','#ff8000','#ff0000']; - if(ngIE) { + var stops = ['#ff0000', '#ff0080', '#ff00ff', '#8000ff', '#0000ff', '#0080ff', '#00ffff', '#00ff80', + '#00ff00', '#80ff00', '#ffff00', '#ff8000', '#ff0000']; + if (ngIE) { var i, div; - for(i=0; i<=11; i++) { - div = $('
').attr('style','height:8.333333%; filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='+stops[i]+', endColorstr='+stops[i+1]+'); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='+stops[i]+', endColorstr='+stops[i+1]+')";'); + for (i=0; i<=11; i++) { + div = $('
').attr('style', + 'height:8.333333%; filter:progid:' + + 'DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=' + stops[i] + + ', endColorstr=' + stops[i + 1] + + '); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=' + + stops[i] + ', endColorstr=' + stops[i + 1] + ')";'); huebar.append(div); } } else { - stopList = stops.join(','); - huebar.attr('style','background:-webkit-linear-gradient(top,'+stopList+'); background: -o-linear-gradient(top,'+stopList+'); background: -ms-linear-gradient(top,'+stopList+'); background:-moz-linear-gradient(top,'+stopList+'); -webkit-linear-gradient(top,'+stopList+'); background:linear-gradient(to bottom,'+stopList+'); '); + var stopList = stops.join(','); + huebar.attr('style', 'background:-webkit-linear-gradient(top,' + stopList + + '); background: -o-linear-gradient(top,' + stopList + + '); background: -ms-linear-gradient(top,' + stopList + + '); background:-moz-linear-gradient(top,' + stopList + + '); -webkit-linear-gradient(top,' + stopList + + '); background:linear-gradient(to bottom,' + stopList + '); '); } cal.find('div.colpick_hue').on('mousedown touchstart',downHue); options.newColor = cal.find('div.colpick_new_color'); options.currentColor = cal.find('div.colpick_current_color'); - //Store options and fill with default color + // Store options and fill with default color cal.data('colpick', options); fillRGBFields(options.color, cal.get(0)); fillHSBFields(options.color, cal.get(0)); @@ -374,7 +428,7 @@ For usage and examples: colpick.com/plugin setSelector(options.color, cal.get(0)); setCurrentColor(options.color, cal.get(0)); setNewColor(options.color, cal.get(0)); - //Append to body if flat=false, else show in place + // Append to body if flat=false, else show in place if (options.flat) { cal.appendTo(this).show(); cal.css({ @@ -391,7 +445,7 @@ For usage and examples: colpick.com/plugin } }); }, - //Shows the picker + // Shows the picker showPicker: function() { return this.each( function () { if ($(this).data('colpickId')) { @@ -399,7 +453,7 @@ For usage and examples: colpick.com/plugin } }); }, - //Hides the picker + // Hides the picker hidePicker: function() { return this.each( function () { if ($(this).data('colpickId')) { @@ -407,14 +461,14 @@ For usage and examples: colpick.com/plugin } }); }, - //Sets a color as new and current (default) + // Sets a color as new and current (default) setColor: function(col, setCurrent) { setCurrent = (typeof setCurrent === "undefined") ? 1 : setCurrent; - if (typeof col == 'string') { + if (typeof col === 'string') { col = hexToHsb(col); - } else if (col.r != undefined && col.g != undefined && col.b != undefined) { + } else if (col.r !== undefined && col.g !== undefined && col.b !== undefined) { col = rgbToHsb(col); - } else if (col.h != undefined && col.s != undefined && col.b != undefined) { + } else if (col.h !== undefined && col.s !== undefined && col.b !== undefined) { col = fixHSB(col); } else { return this; @@ -431,8 +485,9 @@ For usage and examples: colpick.com/plugin setSelector(col, cal.get(0)); setNewColor(col, cal.get(0)); - cal.data('colpick').onChange.apply(cal.parent(), [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 1]); - if(setCurrent) { + cal.data('colpick').onChange.apply(cal.parent(), + [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 1]); + if (setCurrent) { setCurrentColor(col, cal.get(0)); } } @@ -440,13 +495,23 @@ For usage and examples: colpick.com/plugin } }; }(); - //Color space convertions - var hexToRgb = function (hex) { - var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); - return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; + // Color space convertions + var hexToRgb = function (hexString) { + if (typeof hexString !== "string") { + print("Error - ColPick.js::hexToRgb expects string object."); + return; + } + + var hexNumber = parseInt(((hexString.indexOf('#') > -1) ? hexString.substring(1) : hexString), 16); + return { r: hexNumber >> 16, g: (hexNumber & 0x00FF00) >> 8, b: (hexNumber & 0x0000FF)}; }; - var hexToHsb = function (hex) { - return rgbToHsb(hexToRgb(hex)); + var hexToHsb = function (hexString) { + if (typeof hexString !== "string") { + print("Error - ColPick.js::hexToHsb expects string object."); + return; + } + + return rgbToHsb(hexToRgb(hexString)); }; var rgbToHsb = function (rgb) { var hsb = {h: 0, s: 0, b: 0}; @@ -454,14 +519,22 @@ For usage and examples: colpick.com/plugin var max = Math.max(rgb.r, rgb.g, rgb.b); var delta = max - min; hsb.b = max; - hsb.s = max != 0 ? 255 * delta / max : 0; - if (hsb.s != 0) { - if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta; - else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta; - else hsb.h = 4 + (rgb.r - rgb.g) / delta; - } else hsb.h = -1; + hsb.s = max != 0 ? 255 * delta / max : 0; // eslint-disable-line eqeqeq + if (hsb.s != 0) { // eslint-disable-line eqeqeq + if (rgb.r == max) { // eslint-disable-line eqeqeq + hsb.h = (rgb.g - rgb.b) / delta; + } else if (rgb.g == max) { // eslint-disable-line eqeqeq + hsb.h = 2 + (rgb.b - rgb.r) / delta; + } else { + hsb.h = 4 + (rgb.r - rgb.g) / delta; + } + } else { + hsb.h = -1; + } hsb.h *= 60; - if (hsb.h < 0) hsb.h += 360; + if (hsb.h < 0) { + hsb.h += 360; + } hsb.s *= 100/255; hsb.b *= 100/255; return hsb; @@ -471,20 +544,30 @@ For usage and examples: colpick.com/plugin var h = hsb.h; var s = hsb.s*255/100; var v = hsb.b*255/100; - if(s == 0) { + if (s == 0) { // eslint-disable-line eqeqeq rgb.r = rgb.g = rgb.b = v; } else { var t1 = v; var t2 = (255-s)*v/255; var t3 = (t1-t2)*(h%60)/60; - if(h==360) h = 0; - if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} - else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} - else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} - else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} - else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} - else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} - else {rgb.r=0; rgb.g=0; rgb.b=0} + if (h==360) { // eslint-disable-line eqeqeq + h = 0; + } + if (h<60) { + rgb.r=t1; rgb.b=t2; rgb.g=t2+t3; + } else if (h<120) { + rgb.g=t1; rgb.b=t2; rgb.r=t1-t3; + } else if (h<180) { + rgb.g=t1; rgb.r=t2; rgb.b=t2+t3; + } else if (h<240) { + rgb.b=t1; rgb.r=t2; rgb.g=t1-t3; + } else if (h<300) { + rgb.b=t1; rgb.g=t2; rgb.r=t2+t3; + } else if (h<360) { + rgb.r=t1; rgb.g=t2; rgb.b=t1-t3; + } else { + rgb.r=0; rgb.g=0; rgb.b=0; + } } return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; }; @@ -495,7 +578,7 @@ For usage and examples: colpick.com/plugin rgb.b.toString(16) ]; $.each(hex, function (nr, val) { - if (val.length == 1) { + if (val.length == 1) { // eslint-disable-line eqeqeq hex[nr] = '0' + val; } }); @@ -521,3 +604,4 @@ For usage and examples: colpick.com/plugin } }); })(jQuery); + diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 41abc00cff..2b29fbf041 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -31,7 +31,7 @@ var ICON_FOR_TYPE = { var EDITOR_TIMEOUT_DURATION = 1500; var KEY_P = 80; // Key code for letter p used for Parenting hotkey. -var colorPickers = []; +var colorPickers = {}; var lastEntityID = null; var MATERIAL_PREFIX_STRING = "mat::"; @@ -73,8 +73,8 @@ function enableProperties() { function disableProperties() { disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); disableChildren(document, ".colpick"); - for (var i = 0; i < colorPickers.length; i++) { - colorPickers[i].colpickHide(); + for (var pickKey in colorPickers) { + colorPickers[pickKey].colpickHide(); } var elLocked = document.getElementById("property-locked"); @@ -86,7 +86,6 @@ function disableProperties() { function showElements(els, show) { for (var i = 0; i < els.length; i++) { els[i].style.display = (show) ? 'table' : 'none'; - } } @@ -509,15 +508,6 @@ function unbindAllInputs() { } } -function clearSelection() { - if (document.selection && document.selection.empty) { - document.selection.empty(); - } else if (window.getSelection) { - var sel = window.getSelection(); - sel.removeAllRanges(); - } -} - function showParentMaterialNameBox(number, elNumber, elString) { if (number) { $('#property-parent-material-id-number-container').show(); @@ -827,8 +817,10 @@ function loaded() { if (lastEntityID !== '"' + properties.id + '"' && lastEntityID !== null && editor !== null) { saveJSONUserData(true); } - // the event bridge and json parsing handle our avatar id string differently. + var doSelectElement = lastEntityID === '"' + properties.id + '"'; + + // the event bridge and json parsing handle our avatar id string differently. lastEntityID = '"' + properties.id + '"'; elID.value = properties.id; @@ -1193,12 +1185,10 @@ function loaded() { } var activeElement = document.activeElement; - - if (typeof activeElement.select !== "undefined") { + if (doSelectElement && typeof activeElement.select !== "undefined") { activeElement.select(); } } - clearSelection(); } }); } @@ -1381,13 +1371,19 @@ function loaded() { elColorRed.addEventListener('change', colorChangeFunction); elColorGreen.addEventListener('change', colorChangeFunction); elColorBlue.addEventListener('change', colorChangeFunction); - colorPickers.push($('#property-color-control2').colpick({ + colorPickers['#property-color-control2'] = $('#property-color-control2').colpick({ colorScheme: 'dark', layout: 'hex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-color-control2').attr('active', 'true'); + // The original color preview within the picker needs to be updated on show because + // prior to the picker being shown we don't have access to the selections' starting color. + colorPickers['#property-color-control2'].colpickSetColor({ + "r": elColorRed.value, + "g": elColorGreen.value, + "b": elColorBlue.value}); }, onHide: function(colpick) { $('#property-color-control2').attr('active', 'false'); @@ -1396,7 +1392,7 @@ function loaded() { $(el).css('background-color', '#' + hex); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); } - })); + }); elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight')); @@ -1405,13 +1401,20 @@ function loaded() { elLightColorRed.addEventListener('change', lightColorChangeFunction); elLightColorGreen.addEventListener('change', lightColorChangeFunction); elLightColorBlue.addEventListener('change', lightColorChangeFunction); - colorPickers.push($('#property-light-color').colpick({ + colorPickers['#property-light-color'] = $('#property-light-color').colpick({ colorScheme: 'dark', layout: 'hex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-light-color').attr('active', 'true'); + // The original color preview within the picker needs to be updated on show because + // prior to the picker being shown we don't have access to the selections' starting color. + colorPickers['#property-light-color'].colpickSetColor({ + "r": elLightColorRed.value, + "g": elLightColorGreen.value, + "b": elLightColorBlue.value + }); }, onHide: function(colpick) { $('#property-light-color').attr('active', 'false'); @@ -1420,7 +1423,7 @@ function loaded() { $(el).css('background-color', '#' + hex); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); } - })); + }); elLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('intensity', 1)); elLightFalloffRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('falloffRadius', 1)); @@ -1486,13 +1489,20 @@ function loaded() { elTextTextColorRed.addEventListener('change', textTextColorChangeFunction); elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction); elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction); - colorPickers.push($('#property-text-text-color').colpick({ + colorPickers['#property-text-text-color'] = $('#property-text-text-color').colpick({ colorScheme: 'dark', layout: 'hex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-text-text-color').attr('active', 'true'); + // The original color preview within the picker needs to be updated on show because + // prior to the picker being shown we don't have access to the selections' starting color. + colorPickers['#property-text-text-color'].colpickSetColor({ + "r": elTextTextColorRed.value, + "g": elTextTextColorGreen.value, + "b": elTextTextColorBlue.value + }); }, onHide: function(colpick) { $('#property-text-text-color').attr('active', 'false'); @@ -1502,7 +1512,7 @@ function loaded() { $(el).attr('active', 'false'); emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b); } - })); + }); var textBackgroundColorChangeFunction = createEmitColorPropertyUpdateFunction( 'backgroundColor', elTextBackgroundColorRed, elTextBackgroundColorGreen, elTextBackgroundColorBlue); @@ -1510,13 +1520,20 @@ function loaded() { elTextBackgroundColorRed.addEventListener('change', textBackgroundColorChangeFunction); elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction); elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction); - colorPickers.push($('#property-text-background-color').colpick({ + colorPickers['#property-text-background-color'] = $('#property-text-background-color').colpick({ colorScheme: 'dark', layout: 'hex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-text-background-color').attr('active', 'true'); + // The original color preview within the picker needs to be updated on show because + // prior to the picker being shown we don't have access to the selections' starting color. + colorPickers['#property-text-background-color'].colpickSetColor({ + "r": elTextBackgroundColorRed.value, + "g": elTextBackgroundColorGreen.value, + "b": elTextBackgroundColorBlue.value + }); }, onHide: function(colpick) { $('#property-text-background-color').attr('active', 'false'); @@ -1525,7 +1542,7 @@ function loaded() { $(el).css('background-color', '#' + hex); emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b); } - })); + }); // Key light var keyLightModeChanged = createZoneComponentModeChangedFunction('keyLightMode', @@ -1535,13 +1552,20 @@ function loaded() { elZoneKeyLightModeDisabled.addEventListener('change', keyLightModeChanged); elZoneKeyLightModeEnabled.addEventListener('change', keyLightModeChanged); - colorPickers.push($('#property-zone-key-light-color').colpick({ + colorPickers['#property-zone-key-light-color'] = $('#property-zone-key-light-color').colpick({ colorScheme: 'dark', layout: 'hex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-zone-key-light-color').attr('active', 'true'); + // The original color preview within the picker needs to be updated on show because + // prior to the picker being shown we don't have access to the selections' starting color. + colorPickers['#property-zone-key-light-color'].colpickSetColor({ + "r": elZoneKeyLightColorRed.value, + "g": elZoneKeyLightColorGreen.value, + "b": elZoneKeyLightColorBlue.value + }); }, onHide: function(colpick) { $('#property-zone-key-light-color').attr('active', 'false'); @@ -1550,7 +1574,7 @@ function loaded() { $(el).css('background-color', '#' + hex); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight'); } - })); + }); var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight', 'color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); @@ -1604,13 +1628,20 @@ function loaded() { elZoneHazeRange.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeRange')); - colorPickers.push($('#property-zone-haze-color').colpick({ + colorPickers['#property-zone-haze-color'] = $('#property-zone-haze-color').colpick({ colorScheme: 'dark', layout: 'hex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-zone-haze-color').attr('active', 'true'); + // The original color preview within the picker needs to be updated on show because + // prior to the picker being shown we don't have access to the selections' starting color. + colorPickers['#property-zone-haze-color'].colpickSetColor({ + "r": elZoneHazeColorRed.value, + "g": elZoneHazeColorGreen.value, + "b": elZoneHazeColorBlue.value + }); }, onHide: function(colpick) { $('#property-zone-haze-color').attr('active', 'false'); @@ -1619,7 +1650,7 @@ function loaded() { $(el).css('background-color', '#' + hex); emitColorPropertyUpdate('hazeColor', rgb.r, rgb.g, rgb.b, 'haze'); } - })); + }); var zoneHazeColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('haze', 'hazeColor', elZoneHazeColorRed, elZoneHazeColorGreen, @@ -1629,13 +1660,20 @@ function loaded() { elZoneHazeColorGreen.addEventListener('change', zoneHazeColorChangeFunction); elZoneHazeColorBlue.addEventListener('change', zoneHazeColorChangeFunction); - colorPickers.push($('#property-zone-haze-glare-color').colpick({ + colorPickers['#property-zone-haze-glare-color'] = $('#property-zone-haze-glare-color').colpick({ colorScheme: 'dark', layout: 'hex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-zone-haze-glare-color').attr('active', 'true'); + // The original color preview within the picker needs to be updated on show because + // prior to the picker being shown we don't have access to the selections' starting color. + colorPickers['#property-zone-haze-glare-color'].colpickSetColor({ + "r": elZoneHazeGlareColorRed.value, + "g": elZoneHazeGlareColorGreen.value, + "b": elZoneHazeGlareColorBlue.value + }); }, onHide: function(colpick) { $('#property-zone-haze-glare-color').attr('active', 'false'); @@ -1644,7 +1682,7 @@ function loaded() { $(el).css('background-color', '#' + hex); emitColorPropertyUpdate('hazeGlareColor', rgb.r, rgb.g, rgb.b, 'haze'); } - })); + }); var zoneHazeGlareColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('haze', 'hazeGlareColor', elZoneHazeGlareColorRed, elZoneHazeGlareColorGreen, @@ -1671,13 +1709,20 @@ function loaded() { elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction); elZoneSkyboxColorGreen.addEventListener('change', zoneSkyboxColorChangeFunction); elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction); - colorPickers.push($('#property-zone-skybox-color').colpick({ + colorPickers['#property-zone-skybox-color'] = $('#property-zone-skybox-color').colpick({ colorScheme: 'dark', layout: 'hex', color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { $('#property-zone-skybox-color').attr('active', 'true'); + // The original color preview within the picker needs to be updated on show because + // prior to the picker being shown we don't have access to the selections' starting color. + colorPickers['#property-zone-skybox-color'].colpickSetColor({ + "r": elZoneSkyboxColorRed.value, + "g": elZoneSkyboxColorGreen.value, + "b": elZoneSkyboxColorBlue.value + }); }, onHide: function(colpick) { $('#property-zone-skybox-color').attr('active', 'false'); @@ -1686,7 +1731,7 @@ function loaded() { $(el).css('background-color', '#' + hex); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox'); } - })); + }); elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox', 'url')); @@ -1761,34 +1806,13 @@ function loaded() { }; // For input and textarea elements, select all of the text on focus - // WebKit-based browsers, such as is used with QWebView, have a quirk - // where the mouseup event comes after the focus event, causing the - // text to be deselected immediately after selecting all of the text. - // To make this work we block the first mouseup event after the elements - // received focus. If we block all mouseup events the user will not - // be able to click within the selected text. - // We also check to see if the value has changed to make sure we aren't - // blocking a mouse-up event when clicking on an input spinner. var els = document.querySelectorAll("input, textarea"); for (var i = 0; i < els.length; i++) { - var clicked = false; - var originalText; - // TODO FIXME: (JSHint) Functions declared within loops referencing - // an outer scoped variable may lead to confusing semantics. - els[i].onfocus = function(e) { - originalText = this.value; - this.select(); - clicked = false; - }; - // TODO FIXME: (JSHint) Functions declared within loops referencing - // an outer scoped variable may lead to confusing semantics. - els[i].onmouseup = function(e) { - if (!clicked && originalText === this.value) { - e.preventDefault(); - } - clicked = true; + els[i].onfocus = function (e) { + e.target.select(); }; } + bindAllNonJSONEditorElements(); }); diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index a28de5abc2..a34191b951 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -306,10 +306,6 @@ WebTablet.prototype.setScriptURL = function (scriptURL) { Overlays.editOverlay(this.webOverlayID, { scriptURL: scriptURL }); }; -WebTablet.prototype.getOverlayObject = function () { - return Overlays.getOverlayObject(this.webOverlayID); -}; - WebTablet.prototype.setWidth = function (width) { // imported from libraries/utils.js resizeTablet(width); @@ -335,7 +331,7 @@ WebTablet.prototype.destroy = function () { }; WebTablet.prototype.geometryChanged = function (geometry) { - if (!HMD.active) { + if (!HMD.active && HMD.tabletID) { var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); @@ -463,6 +459,9 @@ WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMou }; WebTablet.prototype.onHmdChanged = function () { + if (!HMD.tabletID) { + return; + } var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 44325d4a12..8a07ff0d20 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -21,7 +21,10 @@ SPACE_LOCAL = "local"; SPACE_WORLD = "world"; HIGHLIGHT_LIST_NAME = "editHandleHighlightList"; -Script.include("./controllers.js"); +Script.include([ + "./controllers.js", + "./utils.js" +]); SelectionManager = (function() { var that = {}; @@ -668,7 +671,7 @@ SelectionDisplay = (function() { var pickRay = generalComputePickRay(event.x, event.y); // TODO_Case6491: Move this out to setup just to make it once - var interactiveOverlays = [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]; + var interactiveOverlays = getMainTabletIDs(); for (var key in handleTools) { if (handleTools.hasOwnProperty(key)) { interactiveOverlays.push(key); @@ -681,8 +684,8 @@ SelectionDisplay = (function() { var results = testRayIntersect(pickRay, interactiveOverlays); if (results.intersects) { var hitOverlayID = results.overlayID; - if ((hitOverlayID === HMD.tabletID) || (hitOverlayID === HMD.tabletScreenID) || - (hitOverlayID === HMD.homeButtonID)) { + if ((HMD.tabletID && hitOverlayID === HMD.tabletID) || (HMD.tabletScreenID && hitOverlayID === HMD.tabletScreenID) + || (HMD.homeButtonID && hitOverlayID === HMD.homeButtonID)) { // EARLY EXIT-(mouse clicks on the tablet should override the edit affordances) return false; } diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index bc83cc582c..442a9f6d24 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -428,3 +428,17 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); }; + +getMainTabletIDs = function () { + var tabletIDs = []; + if (HMD.tabletID) { + tabletIDs.push(HMD.tabletID); + } + if (HMD.tabletScreenID) { + tabletIDs.push(HMD.tabletScreenID); + } + if (HMD.homeButtonID) { + tabletIDs.push(HMD.homeButtonID); + } + return tabletIDs; +}; \ No newline at end of file diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index fd1275a251..631b5e97ac 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -75,11 +75,11 @@ var selectionDisplay = null; // for gridTool.js to ignore tablet.pushOntoStack(MARKETPLACE_CHECKOUT_QML_PATH); tablet.sendToQml({ method: 'updateCheckoutQML', params: { - itemId: '0d90d21c-ce7a-4990-ad18-e9d2cf991027', - itemName: 'Test Flaregun', - itemPrice: (debugError ? 10 : 17), - itemHref: 'http://mpassets.highfidelity.com/0d90d21c-ce7a-4990-ad18-e9d2cf991027-v1/flaregun.json', - categories: ["Wearables", "Miscellaneous"] + itemId: '424611a2-73d0-4c03-9087-26a6a279257b', + itemName: '2018-02-15 Finnegon', + itemPrice: (debugError ? 10 : 3), + itemHref: 'http://devmpassets.highfidelity.com/424611a2-73d0-4c03-9087-26a6a279257b-v1/finnigon.fst', + categories: ["Miscellaneous"] } }); } @@ -104,8 +104,9 @@ var selectionDisplay = null; // for gridTool.js to ignore tablet.gotoHomeScreen(); } else { Wallet.refreshWalletStatus(); - var entity = HMD.tabletID; - Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + if (HMD.tabletID) { + Entities.editEntity(HMD.tabletID, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + } showMarketplace(); } } @@ -232,13 +233,19 @@ var selectionDisplay = null; // for gridTool.js to ignore return position; } - function rezEntity(itemHref, isWearable) { + function rezEntity(itemHref, itemType) { + var isWearable = itemType === "wearable"; var success = Clipboard.importEntities(itemHref); var wearableLocalPosition = null; var wearableLocalRotation = null; var wearableLocalDimensions = null; var wearableDimensions = null; + if (itemType === "contentSet") { + console.log("Item is a content set; codepath shouldn't go here.") + return; + } + if (isWearable) { var wearableTransforms = Settings.getValue("io.highfidelity.avatarStore.checkOut.transforms"); if (!wearableTransforms) { @@ -544,7 +551,11 @@ var selectionDisplay = null; // for gridTool.js to ignore break; case 'checkout_rezClicked': case 'purchases_rezClicked': - rezEntity(message.itemHref, message.isWearable); + if (message.itemType === "app") { + console.log("How did you get here? You can't buy apps yet!"); + } else { + rezEntity(message.itemHref, message.itemType); + } break; case 'header_marketplaceImageClicked': case 'purchases_backClicked': diff --git a/scripts/system/menu.js b/scripts/system/menu.js index c7a44d3e48..c27dae6780 100644 --- a/scripts/system/menu.js +++ b/scripts/system/menu.js @@ -28,8 +28,9 @@ var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet- // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); } else { - var entity = HMD.tabletID; - Entities.editEntity(entity, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); + if (HMD.tabletID) { + Entities.editEntity(HMD.tabletID, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + } tablet.gotoMenuScreen(); } } diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 36a1cbcdd9..100d0e82ee 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -41,14 +41,14 @@ if (!UIWebTablet) { return false; } - if (Overlays.getProperty(HMD.tabletID, "type") != "model") { + if (Overlays.getProperty(HMD.tabletID, "type") !== "model") { if (debugTablet) { print("TABLET is invalid due to frame: " + JSON.stringify(Overlays.getProperty(HMD.tabletID, "type"))); } return false; } - if (Overlays.getProperty(HMD.homeButtonID, "type") != "circle3d" || - Overlays.getProperty(HMD.tabletScreenID, "type") != "web3d") { + if (Overlays.getProperty(HMD.homeButtonID, "type") !== "circle3d" || + Overlays.getProperty(HMD.tabletScreenID, "type") !== "web3d") { if (debugTablet) { print("TABLET is invalid due to other"); } @@ -112,7 +112,7 @@ } function showTabletUI() { - checkTablet() + checkTablet(); if (!tabletRezzed || !tabletIsValid()) { closeTabletUI(); @@ -157,7 +157,7 @@ } function closeTabletUI() { - checkTablet() + checkTablet(); gTablet.tabletShown = false; if (UIWebTablet) { if (UIWebTablet.onClose) { @@ -178,14 +178,14 @@ print("TABLET closeTabletUI, UIWebTablet is null"); } tabletRezzed = false; - gTablet = null + gTablet = null; } function updateShowTablet() { var now = Date.now(); - checkTablet() + checkTablet(); // close the WebTablet if it we go into toolbar mode. var tabletShown = gTablet.tabletShown; @@ -270,7 +270,7 @@ } if (channel === "home") { if (UIWebTablet) { - checkTablet() + checkTablet(); gTablet.landscape = false; } } diff --git a/scripts/system/tablet-users.js b/scripts/system/tablet-users.js index 6181173818..92aefd1e80 100644 --- a/scripts/system/tablet-users.js +++ b/scripts/system/tablet-users.js @@ -48,9 +48,8 @@ // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); } else { - var tabletEntity = HMD.tabletID; - if (tabletEntity) { - Entities.editEntity(tabletEntity, {textures: JSON.stringify({"tex.close" : HOME_BUTTON_TEXTURE})}); + if (HMD.tabletID) { + Entities.editEntity(HMD.tabletID, {textures: JSON.stringify({"tex.close" : HOME_BUTTON_TEXTURE})}); } shouldActivateButton = true; tablet.gotoWebScreen(USERS_URL); diff --git a/unpublishedScripts/marketplace/record/html/js/record.js b/unpublishedScripts/marketplace/record/html/js/record.js index aae4d1c89a..39278e9d7e 100644 --- a/unpublishedScripts/marketplace/record/html/js/record.js +++ b/unpublishedScripts/marketplace/record/html/js/record.js @@ -252,16 +252,17 @@ function onFinishOnOpenClicked() { } function signalBodyLoaded() { - EventBridge.emitWebEvent(JSON.stringify({ - type: EVENT_BRIDGE_TYPE, - action: BODY_LOADED_ACTION - })); + var EVENTBRIDGE_OPEN_DELAY = 500; // Delay required to ensure EventBridge is ready for use. + setTimeout(function () { + EventBridge.scriptEventReceived.connect(onScriptEventReceived); + EventBridge.emitWebEvent(JSON.stringify({ + type: EVENT_BRIDGE_TYPE, + action: BODY_LOADED_ACTION + })); + }, EVENTBRIDGE_OPEN_DELAY); } function onBodyLoaded() { - - EventBridge.scriptEventReceived.connect(onScriptEventReceived); - elRecordings = document.getElementById("recordings"); elRecordingsTable = document.getElementById("recordings-table"); diff --git a/unpublishedScripts/marketplace/record/record.js b/unpublishedScripts/marketplace/record/record.js index 68c7ea3f5a..8e49589e3c 100644 --- a/unpublishedScripts/marketplace/record/record.js +++ b/unpublishedScripts/marketplace/record/record.js @@ -139,7 +139,8 @@ } function setMappingCallback(status) { - if (status !== "") { + // FIXME: "" is for RC <= 63, null is for RC > 63. Remove the former when RC63 is no longer used. + if (status !== null && status !== "") { error("Error mapping recording to " + mappingPath + " on Asset Server!", status); return; } diff --git a/unpublishedScripts/marketplace/shapes/modules/laser.js b/unpublishedScripts/marketplace/shapes/modules/laser.js index d5feda0e1f..067b605047 100644 --- a/unpublishedScripts/marketplace/shapes/modules/laser.js +++ b/unpublishedScripts/marketplace/shapes/modules/laser.js @@ -173,8 +173,18 @@ Laser = function (side) { // Normal laser operation with trigger. intersection = Overlays.findRayIntersection(pickRay, PRECISION_PICKING, NO_INCLUDE_IDS, NO_EXCLUDE_IDS, VISIBLE_ONLY); + var tabletIDs = []; + if (HMD.tabletID) { + tabletIDs.push(HMD.tabletID); + } + if (HMD.tabletScreenID) { + tabletIDs.push(HMD.tabletScreenID); + } + if (HMD.homeButtonID) { + tabletIDs.push(HMD.homeButtonID); + } if (Reticle.pointingAtSystemOverlay || (intersection.overlayID - && [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID].indexOf(intersection.overlayID) !== -1)) { + && tabletIDs.indexOf(intersection.overlayID) !== -1)) { // No laser if pointing at HUD overlay or tablet; system provides lasers for these cases. if (isLaserOn) { isLaserOn = false;