Merge branch 'master' of https://github.com/highfidelity/hifi into black

This commit is contained in:
Sam Gateau 2017-09-02 09:28:24 -07:00
commit fd2d246f05
32 changed files with 751 additions and 613 deletions

View file

@ -35,7 +35,7 @@ void ScriptableAvatar::startAnimation(const QString& url, float fps, float prior
return; return;
} }
_animation = DependencyManager::get<AnimationCache>()->getAnimation(url); _animation = DependencyManager::get<AnimationCache>()->getAnimation(url);
_animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame); _animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame, false);
_maskedJoints = maskedJoints; _maskedJoints = maskedJoints;
} }

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 643 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 909 B

View file

@ -29,7 +29,6 @@ Rectangle {
property bool purchasesReceived: false; property bool purchasesReceived: false;
property bool balanceReceived: false; property bool balanceReceived: false;
property bool securityImageResultReceived: false; property bool securityImageResultReceived: false;
property bool keyFilePathIfExistsResultReceived: false;
property string itemId: ""; property string itemId: "";
property string itemHref: ""; property string itemHref: "";
property double balanceAfterPurchase: 0; property double balanceAfterPurchase: 0;
@ -46,10 +45,15 @@ Rectangle {
root.activeView = "needsLogIn"; root.activeView = "needsLogIn";
} else if (isLoggedIn) { } else if (isLoggedIn) {
root.activeView = "initialize"; root.activeView = "initialize";
commerce.getSecurityImage();
commerce.getKeyFilePathIfExists(); commerce.getKeyFilePathIfExists();
commerce.balance(); }
commerce.inventory(); }
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
} }
} }
@ -57,8 +61,8 @@ Rectangle {
securityImageResultReceived = true; securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp"; root.activeView = "notSetUp";
} else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { } else if (exists && root.activeView === "initialize") {
root.activeView = "checkoutMain"; commerce.getWalletAuthenticatedStatus();
} else if (exists) { } else if (exists) {
// just set the source again (to be sure the change was noticed) // just set the source again (to be sure the change was noticed)
securityImage.source = ""; securityImage.source = "";
@ -66,12 +70,17 @@ Rectangle {
} }
} }
onKeyFilePathIfExistsResult: { onWalletAuthenticatedStatusResult: {
keyFilePathIfExistsResultReceived = true; if (!isAuthenticated && !passphraseModal.visible) {
if (path === "" && root.activeView !== "notSetUp") { passphraseModal.visible = true;
root.activeView = "notSetUp"; } else if (isAuthenticated) {
} else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
root.activeView = "checkoutMain"; root.activeView = "checkoutMain";
if (!balanceReceived) {
commerce.balance();
}
if (!purchasesReceived) {
commerce.inventory();
}
} }
} }
@ -89,8 +98,8 @@ Rectangle {
console.log("Failed to get balance", result.data.message); console.log("Failed to get balance", result.data.message);
} else { } else {
root.balanceReceived = true; root.balanceReceived = true;
hfcBalanceText.text = (parseFloat(result.data.balance/100).toFixed(2)) + " HFC"; hfcBalanceText.text = result.data.balance + " HFC";
balanceAfterPurchase = parseFloat(result.data.balance/100) - root.itemPriceFull/100; balanceAfterPurchase = result.data.balance - root.itemPriceFull;
root.setBuyText(); root.setBuyText();
} }
} }
@ -110,10 +119,6 @@ Rectangle {
} }
} }
HifiWallet.SecurityImageModel {
id: securityImageModel;
}
// //
// TITLE BAR START // TITLE BAR START
// //
@ -195,7 +200,6 @@ Rectangle {
securityImageResultReceived = false; securityImageResultReceived = false;
purchasesReceived = false; purchasesReceived = false;
balanceReceived = false; balanceReceived = false;
keyFilePathIfExistsResultReceived = false;
commerce.getLoginStatus(); commerce.getLoginStatus();
} }
} }
@ -221,7 +225,20 @@ Rectangle {
} }
} }
HifiWallet.PassphraseModal {
id: passphraseModal;
visible: false;
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
// //
// "WALLET NOT SET UP" START // "WALLET NOT SET UP" START
@ -553,7 +570,7 @@ Rectangle {
} }
RalewayRegular { RalewayRegular {
id: balanceAfterPurchaseText; id: balanceAfterPurchaseText;
text: balanceAfterPurchase.toFixed(2) + " HFC"; text: balanceAfterPurchase + " HFC";
// Text size // Text size
size: balanceAfterPurchaseTextLabel.size; size: balanceAfterPurchaseTextLabel.size;
// Anchors // Anchors
@ -648,7 +665,7 @@ Rectangle {
sendToScript({method: 'checkout_goToPurchases'}); sendToScript({method: 'checkout_goToPurchases'});
} }
} }
RalewayRegular { RalewayRegular {
id: buyText; id: buyText;
// Text size // Text size
@ -687,7 +704,7 @@ Rectangle {
anchors.bottom: root.bottom; anchors.bottom: root.bottom;
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
RalewayRegular { RalewayRegular {
id: completeText; id: completeText;
text: "<b>Purchase Complete!</b><br><br>You bought <b>" + (itemNameText.text) + "</b> by <b>" + (itemAuthorText.text) + "</b>"; text: "<b>Purchase Complete!</b><br><br>You bought <b>" + (itemNameText.text) + "</b> by <b>" + (itemAuthorText.text) + "</b>";
@ -706,7 +723,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
} }
Item { Item {
id: checkoutSuccessActionButtonsContainer; id: checkoutSuccessActionButtonsContainer;
// Size // Size
@ -756,7 +773,7 @@ Rectangle {
} }
} }
} }
Item { Item {
id: continueShoppingButtonContainer; id: continueShoppingButtonContainer;
// Size // Size
@ -799,7 +816,7 @@ Rectangle {
anchors.bottom: root.bottom; anchors.bottom: root.bottom;
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
RalewayRegular { RalewayRegular {
id: failureHeaderText; id: failureHeaderText;
text: "<b>Purchase Failed.</b><br>Your Purchases and HFC balance haven't changed."; text: "<b>Purchase Failed.</b><br>Your Purchases and HFC balance haven't changed.";
@ -818,7 +835,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
} }
RalewayRegular { RalewayRegular {
id: failureErrorText; id: failureErrorText;
// Text size // Text size
@ -836,7 +853,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
} }
Item { Item {
id: backToMarketplaceButtonContainer; id: backToMarketplaceButtonContainer;
// Size // Size
@ -892,7 +909,7 @@ Rectangle {
itemNameText.text = message.params.itemName; itemNameText.text = message.params.itemName;
itemAuthorText.text = message.params.itemAuthor; itemAuthorText.text = message.params.itemAuthor;
root.itemPriceFull = message.params.itemPrice; root.itemPriceFull = message.params.itemPrice;
itemPriceText.text = root.itemPriceFull === 0 ? "Free" : "<b>" + (parseFloat(root.itemPriceFull/100).toFixed(2)) + " HFC</b>"; itemPriceText.text = root.itemPriceFull === 0 ? "Free" : "<b>" + root.itemPriceFull + " HFC</b>";
itemHref = message.params.itemHref; itemHref = message.params.itemHref;
if (itemHref.indexOf('.json') === -1) { if (itemHref.indexOf('.json') === -1) {
root.itemIsJson = false; root.itemIsJson = false;

View file

@ -28,7 +28,6 @@ Rectangle {
property string activeView: "initialize"; property string activeView: "initialize";
property string referrerURL: ""; property string referrerURL: "";
property bool securityImageResultReceived: false; property bool securityImageResultReceived: false;
property bool keyFilePathIfExistsResultReceived: false;
property bool purchasesReceived: false; property bool purchasesReceived: false;
property bool punctuationMode: false; property bool punctuationMode: false;
// Style // Style
@ -41,9 +40,15 @@ Rectangle {
root.activeView = "needsLogIn"; root.activeView = "needsLogIn";
} else if (isLoggedIn) { } else if (isLoggedIn) {
root.activeView = "initialize"; root.activeView = "initialize";
commerce.getSecurityImage();
commerce.getKeyFilePathIfExists(); commerce.getKeyFilePathIfExists();
commerce.inventory(); }
}
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
} }
} }
@ -51,8 +56,8 @@ Rectangle {
securityImageResultReceived = true; securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp"; root.activeView = "notSetUp";
} else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { } else if (exists && root.activeView === "initialize") {
root.activeView = "purchasesMain"; commerce.getWalletAuthenticatedStatus();
} else if (exists) { } else if (exists) {
// just set the source again (to be sure the change was noticed) // just set the source again (to be sure the change was noticed)
securityImage.source = ""; securityImage.source = "";
@ -60,12 +65,12 @@ Rectangle {
} }
} }
onKeyFilePathIfExistsResult: { onWalletAuthenticatedStatusResult: {
keyFilePathIfExistsResultReceived = true; if (!isAuthenticated && !passphraseModal.visible) {
if (path === "" && root.activeView !== "notSetUp") { passphraseModal.visible = true;
root.activeView = "notSetUp"; } else if (isAuthenticated) {
} else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
root.activeView = "purchasesMain"; root.activeView = "purchasesMain";
commerce.inventory();
} }
} }
@ -166,7 +171,6 @@ Rectangle {
Component.onCompleted: { Component.onCompleted: {
securityImageResultReceived = false; securityImageResultReceived = false;
purchasesReceived = false; purchasesReceived = false;
keyFilePathIfExistsResultReceived = false;
commerce.getLoginStatus(); commerce.getLoginStatus();
} }
} }
@ -191,6 +195,21 @@ Rectangle {
commerce.getLoginStatus(); commerce.getLoginStatus();
} }
} }
HifiWallet.PassphraseModal {
id: passphraseModal;
visible: false;
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
// //
// "WALLET NOT SET UP" START // "WALLET NOT SET UP" START

View file

@ -47,11 +47,26 @@ Item {
HifiControlsUit.Button { HifiControlsUit.Button {
color: hifi.buttons.black; color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark; colorScheme: hifi.colorSchemes.dark;
anchors.bottom: helpText.bottom; anchors.bottom: resetButton.top;
anchors.bottomMargin: 15;
anchors.horizontalCenter: parent.horizontalCenter; anchors.horizontalCenter: parent.horizontalCenter;
height: 50; height: 50;
width: 250; width: 250;
text: "Testing: Reset Wallet!"; text: "DEBUG: Clear Cached Passphrase";
onClicked: {
commerce.setPassphrase("");
}
}
HifiControlsUit.Button {
id: resetButton;
color: hifi.buttons.red;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: helpText.bottom;
anchors.bottomMargin: 15;
anchors.horizontalCenter: parent.horizontalCenter;
height: 50;
width: 250;
text: "DEBUG: Reset Wallet!";
onClicked: { onClicked: {
commerce.reset(); commerce.reset();
sendSignalToWallet({method: 'walletReset'}); sendSignalToWallet({method: 'walletReset'});

View file

@ -0,0 +1,304 @@
//
// PassphraseModal.qml
// qml/hifi/commerce/wallet
//
// PassphraseModal
//
// Created by Zach Fox on 2017-08-31
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
z: 998;
property bool keyboardRaised: false;
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
passphraseModalSecurityImage.source = "";
passphraseModalSecurityImage.source = "image://security/securityImage";
}
onWalletAuthenticatedStatusResult: {
submitPassphraseInputButton.enabled = true;
if (!isAuthenticated) {
errorText.text = "Authentication failed - please try again.";
} else {
root.visible = false;
}
}
}
// 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.
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
// This will cause a bug -- if you bring up passphrase selection in HUD mode while
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
// HMD preview will stay off.
// TODO: Fix this unlikely bug
onVisibleChanged: {
if (visible) {
passphraseField.focus = true;
sendSignalToParent({method: 'disableHmdPreview'});
} else {
sendSignalToParent({method: 'maybeEnableHmdPreview'});
}
}
// Background rectangle
Rectangle {
anchors.fill: parent;
color: "black";
opacity: 0.9;
}
Rectangle {
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 250;
color: hifi.colors.baseGray;
RalewaySemiBold {
id: instructionsText;
text: "Enter Wallet Passphrase";
size: 16;
anchors.top: parent.top;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.leftMargin: 16;
width: passphraseField.width;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignLeft;
}
HifiControlsUit.TextField {
id: passphraseField;
anchors.top: instructionsText.bottom;
anchors.topMargin: 4;
anchors.left: instructionsText.left;
width: 280;
height: 50;
echoMode: TextInput.Password;
placeholderText: "passphrase";
onFocusChanged: {
root.keyboardRaised = focus;
}
MouseArea {
anchors.fill: parent;
onClicked: {
parent.focus = true;
root.keyboardRaised = true;
}
}
onAccepted: {
submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text);
}
}
// Show passphrase text
HifiControlsUit.CheckBox {
id: showPassphrase;
colorScheme: hifi.colorSchemes.dark;
anchors.left: passphraseField.left;
anchors.top: passphraseField.bottom;
anchors.topMargin: 8;
height: 30;
text: "Show passphrase as plain text";
boxSize: 24;
onClicked: {
passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
}
}
// Security Image
Item {
id: securityImageContainer;
// Anchors
anchors.top: instructionsText.top;
anchors.left: passphraseField.right;
anchors.leftMargin: 12;
anchors.right: parent.right;
Image {
id: passphraseModalSecurityImage;
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: 75;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
cache: false;
onVisibleChanged: {
commerce.getSecurityImage();
}
}
Image {
id: passphraseModalSecurityImageOverlay;
source: "images/lockOverlay.png";
width: passphraseModalSecurityImage.width * 0.45;
height: passphraseModalSecurityImage.height * 0.45;
anchors.bottom: passphraseModalSecurityImage.bottom;
anchors.right: passphraseModalSecurityImage.right;
mipmap: true;
opacity: 0.9;
}
// "Security image" text below pic
RalewayRegular {
text: "security image";
// Text size
size: 12;
// Anchors
anchors.top: passphraseModalSecurityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// Error text above buttons
RalewaySemiBold {
id: errorText;
text: "";
// Text size
size: 16;
// Anchors
anchors.bottom: passphrasePopupActionButtonsContainer.top;
anchors.bottomMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 30;
// Style
color: hifi.colors.redHighlight;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
//
// ACTION BUTTONS START
//
Item {
id: passphrasePopupActionButtonsContainer;
// Size
width: root.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 10;
// "Cancel" button
HifiControlsUit.Button {
id: cancelPassphraseInputButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
height: 40;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: "Cancel"
onClicked: {
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
}
}
// "Submit" button
HifiControlsUit.Button {
id: submitPassphraseInputButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
height: 40;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
text: "Submit"
onClicked: {
submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text);
}
}
}
}
Item {
id: keyboardContainer;
z: 999;
visible: keyboard.raised;
property bool punctuationMode: false;
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
}
Image {
id: lowerKeyboardButton;
source: "images/lowerKeyboard.png";
anchors.horizontalCenter: parent.horizontalCenter;
anchors.bottom: keyboard.top;
height: 30;
width: 120;
MouseArea {
anchors.fill: parent;
onClicked: {
root.keyboardRaised = false;
}
}
}
HifiControlsUit.Keyboard {
id: keyboard;
raised: HMD.mounted && root.keyboardRaised;
numeric: parent.punctuationMode;
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
}
}
}
signal sendSignalToParent(var msg);
}

View file

@ -40,8 +40,8 @@ Item {
passphrasePageSecurityImage.source = "image://security/securityImage"; passphrasePageSecurityImage.source = "image://security/securityImage";
} }
onPassphraseSetupStatusResult: { onWalletAuthenticatedStatusResult: {
sendMessageToLightbox({method: 'statusResult', status: passphraseIsSetup}); sendMessageToLightbox({method: 'statusResult', status: isAuthenticated});
} }
} }
@ -58,10 +58,6 @@ Item {
} }
} }
SecurityImageModel {
id: gridModel;
}
HifiControlsUit.TextField { HifiControlsUit.TextField {
id: passphraseField; id: passphraseField;
anchors.top: parent.top; anchors.top: parent.top;
@ -199,7 +195,7 @@ Item {
// Text below TextFields // Text below TextFields
RalewaySemiBold { RalewaySemiBold {
id: passwordReqs; id: passwordReqs;
text: "Passphrase must be at least 4 characters"; text: "Passphrase must be at least 3 characters";
// Text size // Text size
size: 16; size: 16;
// Anchors // Anchors
@ -256,7 +252,7 @@ Item {
} }
function validateAndSubmitPassphrase() { function validateAndSubmitPassphrase() {
if (passphraseField.text.length < 4) { if (passphraseField.text.length < 3) {
setErrorText("Passphrase too short."); setErrorText("Passphrase too short.");
return false; return false;
} else if (passphraseField.text !== passphraseFieldAgain.text) { } else if (passphraseField.text !== passphraseFieldAgain.text) {

View file

@ -26,8 +26,6 @@ Rectangle {
id: root; id: root;
property string activeView: "initialize"; property string activeView: "initialize";
property bool securityImageResultReceived: false;
property bool keyFilePathIfExistsResultReceived: false;
property bool keyboardRaised: false; property bool keyboardRaised: false;
// Style // Style
@ -40,25 +38,30 @@ Rectangle {
root.activeView = "needsLogIn"; root.activeView = "needsLogIn";
} else if (isLoggedIn) { } else if (isLoggedIn) {
root.activeView = "initialize"; root.activeView = "initialize";
commerce.getSecurityImage();
commerce.getKeyFilePathIfExists(); commerce.getKeyFilePathIfExists();
} }
} }
onSecurityImageResult: { onKeyFilePathIfExistsResult: {
securityImageResultReceived = true; if (path === "" && root.activeView !== "notSetUp") {
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp"; root.activeView = "notSetUp";
} else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { } else if (path !== "" && root.activeView === "initialize") {
root.activeView = "walletHome"; commerce.getSecurityImage();
} }
} }
onKeyFilePathIfExistsResult: { onSecurityImageResult: {
keyFilePathIfExistsResultReceived = true; if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp"; root.activeView = "notSetUp";
} else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") { } else if (exists && root.activeView === "initialize") {
commerce.getWalletAuthenticatedStatus();
}
}
onWalletAuthenticatedStatusResult: {
if (!isAuthenticated && !passphraseModal.visible) {
passphraseModal.visible = true;
} else if (isAuthenticated) {
root.activeView = "walletHome"; root.activeView = "walletHome";
} }
} }
@ -89,7 +92,8 @@ Rectangle {
if (msg.method === 'walletSetup_cancelClicked') { if (msg.method === 'walletSetup_cancelClicked') {
walletSetupLightbox.visible = false; walletSetupLightbox.visible = false;
} else if (msg.method === 'walletSetup_finished') { } else if (msg.method === 'walletSetup_finished') {
root.activeView = "walletHome"; root.activeView = "initialize";
commerce.getLoginStatus();
} else if (msg.method === 'walletSetup_raiseKeyboard') { } else if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true; root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') { } else if (msg.method === 'walletSetup_lowerKeyboard') {
@ -218,6 +222,21 @@ Rectangle {
} }
} }
PassphraseModal {
id: passphraseModal;
visible: false;
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
NotSetUp { NotSetUp {
id: notSetUp; id: notSetUp;
visible: root.activeView === "notSetUp"; visible: root.activeView === "notSetUp";

View file

@ -38,7 +38,7 @@ Item {
} }
onBalanceResult : { onBalanceResult : {
balanceText.text = parseFloat(result.data.balance/100).toFixed(2); balanceText.text = result.data.balance;
} }
onHistoryResult : { onHistoryResult : {
@ -88,13 +88,14 @@ Item {
height: 60; height: 60;
Rectangle { Rectangle {
id: hfcBalanceField; id: hfcBalanceField;
color: hifi.colors.darkGray;
anchors.right: parent.right; anchors.right: parent.right;
anchors.left: parent.left; anchors.left: parent.left;
anchors.bottom: parent.bottom; anchors.bottom: parent.bottom;
height: parent.height - 15; height: parent.height - 15;
// "HFC" balance label // "HFC" balance label
RalewayRegular { FiraSansRegular {
id: balanceLabel; id: balanceLabel;
text: "HFC"; text: "HFC";
// Text size // Text size
@ -106,7 +107,7 @@ Item {
anchors.rightMargin: 4; anchors.rightMargin: 4;
width: paintedWidth; width: paintedWidth;
// Style // Style
color: hifi.colors.darkGray; color: hifi.colors.lightGrayText;
// Alignment // Alignment
horizontalAlignment: Text.AlignRight; horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
@ -121,7 +122,7 @@ Item {
} }
// Balance Text // Balance Text
FiraSansRegular { FiraSansSemiBold {
id: balanceText; id: balanceText;
text: "--"; text: "--";
// Text size // Text size
@ -133,7 +134,7 @@ Item {
anchors.right: balanceLabel.left; anchors.right: balanceLabel.left;
anchors.rightMargin: 4; anchors.rightMargin: 4;
// Style // Style
color: hifi.colors.darkGray; color: hifi.colors.lightGrayText;
// Alignment // Alignment
horizontalAlignment: Text.AlignRight; horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
@ -258,7 +259,7 @@ Item {
delegate: Item { delegate: Item {
width: parent.width; width: parent.width;
height: transactionText.height + 30; height: transactionText.height + 30;
RalewayRegular { FiraSansRegular {
id: transactionText; id: transactionText;
text: model.text; text: model.text;
// Style // Style
@ -272,7 +273,7 @@ Item {
horizontalAlignment: Text.AlignLeft; horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
} }
HifiControlsUit.Separator { HifiControlsUit.Separator {
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
@ -288,7 +289,7 @@ Item {
} }
// This should never be visible (since you immediately get 100 HFC) // This should never be visible (since you immediately get 100 HFC)
RalewayRegular { FiraSansRegular {
id: emptyTransationHistory; id: emptyTransationHistory;
size: 24; size: 24;
visible: !transactionHistory.visible && root.historyReceived; visible: !transactionHistory.visible && root.historyReceived;

View file

@ -39,9 +39,9 @@ Rectangle {
} }
} }
onPassphraseSetupStatusResult: { onWalletAuthenticatedStatusResult: {
securityImageContainer.visible = false; securityImageContainer.visible = false;
if (passphraseIsSetup) { if (isAuthenticated) {
privateKeysReadyContainer.visible = true; privateKeysReadyContainer.visible = true;
} else { } else {
choosePassphraseContainer.visible = true; choosePassphraseContainer.visible = true;
@ -117,7 +117,7 @@ Rectangle {
anchors.right: parent.right; anchors.right: parent.right;
anchors.rightMargin: 16; anchors.rightMargin: 16;
height: 280; height: 280;
Connections { Connections {
onSendSignalToWallet: { onSendSignalToWallet: {
sendSignalToWallet(msg); sendSignalToWallet(msg);
@ -210,7 +210,7 @@ Rectangle {
onVisibleChanged: { onVisibleChanged: {
if (visible) { if (visible) {
commerce.getPassphraseSetupStatus(); commerce.getWalletAuthenticatedStatus();
} }
} }

View file

@ -11,6 +11,7 @@
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QTimeZone>
#include <QJsonDocument> #include <QJsonDocument>
#include "AccountManager.h" #include "AccountManager.h"
#include "Wallet.h" #include "Wallet.h"
@ -45,7 +46,6 @@ Handler(buy)
Handler(receiveAt) Handler(receiveAt)
Handler(balance) Handler(balance)
Handler(inventory) Handler(inventory)
Handler(history)
void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request) { void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request) {
auto accountManager = DependencyManager::get<AccountManager>(); auto accountManager = DependencyManager::get<AccountManager>();
@ -108,6 +108,64 @@ void Ledger::inventory(const QStringList& keys) {
keysQuery("inventory", "inventorySuccess", "inventoryFailure"); keysQuery("inventory", "inventorySuccess", "inventoryFailure");
} }
QString nameFromKey(const QString& key, const QStringList& publicKeys) {
if (key.isNull() || key.isEmpty()) {
return "<b>Marketplace</b>";
}
if (publicKeys.contains(key)) {
return "You";
}
return "<b>Someone</b>";
}
void Ledger::historySuccess(QNetworkReply& reply) {
// here we send a historyResult with some extra stuff in it
// Namely, the styled text we'd like to show. The issue is the
// QML cannot do that easily since it doesn't know what the wallet
// public key(s) are. Let's keep it that way
QByteArray response = reply.readAll();
QJsonObject data = QJsonDocument::fromJson(response).object();
// we will need the array of public keys from the wallet
auto wallet = DependencyManager::get<Wallet>();
auto keys = wallet->listPublicKeys();
// now we need to loop through the transactions and add fancy text...
auto historyArray = data.find("data").value().toObject().find("history").value().toArray();
QJsonArray newHistoryArray;
// TODO: do this with 0 copies if possible
for(auto it = historyArray.begin(); it != historyArray.end(); it++) {
auto valueObject = (*it).toObject();
QString from = nameFromKey(valueObject["sender_key"].toString(), keys);
QString to = nameFromKey(valueObject["recipient_key"].toString(), keys);
// turns out on my machine, toLocalTime convert to some weird timezone, yet the
// systemTimeZone is correct. To avoid a strange bug with other's systems too, lets
// be explicit
#ifdef Q_OS_MAC
QDateTime createdAt = QDateTime::fromTime_t(valueObject["created_at"].toInt(), Qt::UTC);
#else
QDateTime createdAt = QDateTime::fromSecsSinceEpoch(valueObject["created_at"].toInt(), Qt::UTC);
#endif
QDateTime localCreatedAt = createdAt.toTimeZone(QTimeZone::systemTimeZone());
valueObject["text"] = QString("%1 sent %2 <b>%3 %4</b> on %5 with message \"%6\"").
arg(from, to, QString::number(valueObject["quantity"].toInt()), valueObject["asset_title"].toString(), localCreatedAt.toString(Qt::SystemLocaleShortDate), valueObject["message"].toString());
newHistoryArray.push_back(valueObject);
}
// now copy the rest of the json -- this is inefficient
// TODO: try to do this without making copies
QJsonObject newData;
newData["status"] = "success";
QJsonObject newDataData;
newDataData["history"] = newHistoryArray;
newData["data"] = newDataData;
emit historyResult(newData);
}
void Ledger::historyFailure(QNetworkReply& reply) {
failResponse("history", reply);
}
void Ledger::history(const QStringList& keys) { void Ledger::history(const QStringList& keys) {
keysQuery("history", "historySuccess", "historyFailure"); keysQuery("history", "historySuccess", "historyFailure");
} }
@ -117,4 +175,4 @@ void Ledger::resetSuccess(QNetworkReply& reply) { apiResponse("reset", reply); }
void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); } void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); }
void Ledger::reset() { void Ledger::reset() {
send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, QJsonObject()); send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, QJsonObject());
} }

View file

@ -29,6 +29,30 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult); connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult);
} }
void QmlCommerce::getLoginStatus() {
emit loginStatusResult(DependencyManager::get<AccountManager>()->isLoggedIn());
}
void QmlCommerce::getKeyFilePathIfExists() {
auto wallet = DependencyManager::get<Wallet>();
wallet->sendKeyFilePathIfExists();
}
void QmlCommerce::getWalletAuthenticatedStatus() {
auto wallet = DependencyManager::get<Wallet>();
emit walletAuthenticatedStatusResult(wallet->walletIsAuthenticatedWithPassphrase());
}
void QmlCommerce::getSecurityImage() {
auto wallet = DependencyManager::get<Wallet>();
wallet->getSecurityImage();
}
void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
auto wallet = DependencyManager::get<Wallet>();
wallet->chooseSecurityImage(imageFile);
}
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) { void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
auto ledger = DependencyManager::get<Ledger>(); auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
@ -60,30 +84,14 @@ void QmlCommerce::history() {
ledger->history(wallet->listPublicKeys()); ledger->history(wallet->listPublicKeys());
} }
void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
auto wallet = DependencyManager::get<Wallet>();
wallet->chooseSecurityImage(imageFile);
}
void QmlCommerce::getSecurityImage() {
auto wallet = DependencyManager::get<Wallet>();
wallet->getSecurityImage();
}
void QmlCommerce::getLoginStatus() {
emit loginStatusResult(DependencyManager::get<AccountManager>()->isLoggedIn());
}
void QmlCommerce::setPassphrase(const QString& passphrase) { void QmlCommerce::setPassphrase(const QString& passphrase) {
emit passphraseSetupStatusResult(true);
}
void QmlCommerce::getPassphraseSetupStatus() {
emit passphraseSetupStatusResult(false);
}
void QmlCommerce::getKeyFilePathIfExists() {
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
wallet->sendKeyFilePathIfExists(); if (wallet->getPassphrase() && !wallet->getPassphrase()->isEmpty()) {
wallet->changePassphrase(passphrase);
} else {
wallet->setPassphrase(passphrase);
}
getWalletAuthenticatedStatus();
} }
void QmlCommerce::reset() { void QmlCommerce::reset() {
@ -91,4 +99,4 @@ void QmlCommerce::reset() {
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
ledger->reset(); ledger->reset();
wallet->reset(); wallet->reset();
} }

View file

@ -28,28 +28,32 @@ public:
QmlCommerce(QQuickItem* parent = nullptr); QmlCommerce(QQuickItem* parent = nullptr);
signals: signals:
void loginStatusResult(bool isLoggedIn);
void keyFilePathIfExistsResult(const QString& path);
void securityImageResult(bool exists);
void walletAuthenticatedStatusResult(bool isAuthenticated);
void buyResult(QJsonObject result); void buyResult(QJsonObject result);
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and // Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain). // because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
void balanceResult(QJsonObject result); void balanceResult(QJsonObject result);
void inventoryResult(QJsonObject result); void inventoryResult(QJsonObject result);
void securityImageResult(bool exists);
void loginStatusResult(bool isLoggedIn);
void passphraseSetupStatusResult(bool passphraseIsSetup);
void historyResult(QJsonObject result); void historyResult(QJsonObject result);
void keyFilePathIfExistsResult(const QString& path);
protected: protected:
Q_INVOKABLE void getLoginStatus();
Q_INVOKABLE void getKeyFilePathIfExists();
Q_INVOKABLE void getSecurityImage();
Q_INVOKABLE void getWalletAuthenticatedStatus();
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
Q_INVOKABLE void setPassphrase(const QString& passphrase);
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = ""); Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
Q_INVOKABLE void balance(); Q_INVOKABLE void balance();
Q_INVOKABLE void inventory(); Q_INVOKABLE void inventory();
Q_INVOKABLE void history(); Q_INVOKABLE void history();
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
Q_INVOKABLE void getSecurityImage();
Q_INVOKABLE void getLoginStatus();
Q_INVOKABLE void setPassphrase(const QString& passphrase);
Q_INVOKABLE void getPassphraseSetupStatus();
Q_INVOKABLE void getKeyFilePathIfExists();
Q_INVOKABLE void reset(); Q_INVOKABLE void reset();
}; };

View file

@ -56,16 +56,72 @@ QString imageFilePath() {
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
// just return a hardcoded pwd for now // just return a hardcoded pwd for now
auto passphrase = DependencyManager::get<Wallet>()->getPassphrase(); auto passphrase = DependencyManager::get<Wallet>()->getPassphrase();
if (passphrase) { if (passphrase && !passphrase->isEmpty()) {
strcpy(password, passphrase->toLocal8Bit().constData()); strcpy(password, passphrase->toLocal8Bit().constData());
return static_cast<int>(passphrase->size()); return static_cast<int>(passphrase->size());
} else { } else {
// ok gotta bring up modal dialog... But right now lets just // this shouldn't happen - so lets log it to tell us we have
// just keep it empty // a problem with the flow...
qCCritical(commerce) << "no cached passphrase while decrypting!";
return 0; return 0;
} }
} }
RSA* readKeys(const char* filename) {
FILE* fp;
RSA* key = NULL;
if ((fp = fopen(filename, "rt"))) {
// file opened successfully
qCDebug(commerce) << "opened key file" << filename;
if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) {
// now read private key
qCDebug(commerce) << "read public key";
if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
qCDebug(commerce) << "read private key";
fclose(fp);
return key;
}
qCDebug(commerce) << "failed to read private key";
} else {
qCDebug(commerce) << "failed to read public key";
}
fclose(fp);
} else {
qCDebug(commerce) << "failed to open key file" << filename;
}
return key;
}
bool writeKeys(const char* filename, RSA* keys) {
FILE* fp;
bool retval = false;
if ((fp = fopen(filename, "wt"))) {
if (!PEM_write_RSAPublicKey(fp, keys)) {
fclose(fp);
qCDebug(commerce) << "failed to write public key";
QFile(QString(filename)).remove();
return retval;
}
if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
fclose(fp);
qCDebug(commerce) << "failed to write private key";
QFile(QString(filename)).remove();
return retval;
}
retval = true;
qCDebug(commerce) << "wrote keys successfully";
fclose(fp);
} else {
qCDebug(commerce) << "failed to open key file" << filename;
}
return retval;
}
// BEGIN copied code - this will be removed/changed at some point soon // BEGIN copied code - this will be removed/changed at some point soon
// copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp. // copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp.
// We will have a different implementation in practice, but this gives us a start for now // We will have a different implementation in practice, but this gives us a start for now
@ -124,25 +180,9 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
} }
if (!writeKeys(keyFilePath().toStdString().c_str(), keyPair)) {
// now lets persist them to files qCDebug(commerce) << "couldn't save keys!";
// FIXME: for now I'm appending to the file if it exists. As long as we always put return retval;
// the keys in the same order, this works fine. TODO: verify this will skip over
// anything else (like an embedded image)
FILE* fp;
if ((fp = fopen(keyFilePath().toStdString().c_str(), "at"))) {
if (!PEM_write_RSAPublicKey(fp, keyPair)) {
fclose(fp);
qCDebug(commerce) << "failed to write public key";
return retval;
}
if (!PEM_write_RSAPrivateKey(fp, keyPair, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
fclose(fp);
qCDebug(commerce) << "failed to write private key";
return retval;
}
fclose(fp);
} }
RSA_free(keyPair); RSA_free(keyPair);
@ -201,9 +241,6 @@ RSA* readPrivateKey(const char* filename) {
// file opened successfully // file opened successfully
qCDebug(commerce) << "opened key file" << filename; qCDebug(commerce) << "opened key file" << filename;
if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) { if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
// cleanup
fclose(fp);
qCDebug(commerce) << "parsed private key file successfully"; qCDebug(commerce) << "parsed private key file successfully";
} else { } else {
@ -215,7 +252,6 @@ RSA* readPrivateKey(const char* filename) {
} }
return key; return key;
} }
static const unsigned char IVEC[16] = "IAmAnIVecYay123"; static const unsigned char IVEC[16] = "IAmAnIVecYay123";
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) { void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
@ -341,6 +377,24 @@ bool Wallet::decryptFile(const QString& inputFilePath, unsigned char** outputBuf
return true; return true;
} }
bool Wallet::walletIsAuthenticatedWithPassphrase() {
// try to read existing keys if they exist...
// FIXME: initialize OpenSSL elsewhere soon
initialize();
auto publicKey = readPublicKey(keyFilePath().toStdString().c_str());
if (publicKey.size() > 0) {
if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) {
RSA_free(key);
return true;
}
}
return false;
}
bool Wallet::createIfNeeded() { bool Wallet::createIfNeeded() {
if (_publicKeys.count() > 0) return false; if (_publicKeys.count() > 0) return false;
@ -512,3 +566,30 @@ void Wallet::reset() {
keyFile.remove(); keyFile.remove();
imageFile.remove(); imageFile.remove();
} }
bool Wallet::changePassphrase(const QString& newPassphrase) {
qCDebug(commerce) << "changing passphrase";
RSA* keys = readKeys(keyFilePath().toStdString().c_str());
if (keys) {
// we read successfully, so now write to a new temp file
// save old passphrase just in case
// TODO: force re-enter?
QString oldPassphrase = *_passphrase;
setPassphrase(newPassphrase);
QString tempFileName = QString("%1.%2").arg(keyFilePath(), QString("temp"));
if (writeKeys(tempFileName.toStdString().c_str(), keys)) {
// ok, now move the temp file to the correct spot
QFile(QString(keyFilePath())).remove();
QFile(tempFileName).rename(QString(keyFilePath()));
qCDebug(commerce) << "passphrase changed successfully";
return true;
} else {
qCDebug(commerce) << "couldn't write keys";
QFile(tempFileName).remove();
setPassphrase(oldPassphrase);
return false;
}
}
qCDebug(commerce) << "couldn't read keys";
return false;
}

View file

@ -39,18 +39,21 @@ public:
void setPassphrase(const QString& passphrase); void setPassphrase(const QString& passphrase);
QString* getPassphrase() { return _passphrase; } QString* getPassphrase() { return _passphrase; }
bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); }
bool walletIsAuthenticatedWithPassphrase();
bool changePassphrase(const QString& newPassphrase);
void reset(); void reset();
signals: signals:
void securityImageResult(bool exists) ; void securityImageResult(bool exists);
void keyFilePathIfExistsResult(const QString& path); void keyFilePathIfExistsResult(const QString& path);
private: private:
QStringList _publicKeys{}; QStringList _publicKeys{};
QPixmap* _securityImage { nullptr }; QPixmap* _securityImage { nullptr };
QByteArray _salt {"iamsalt!"}; QByteArray _salt {"iamsalt!"};
QString* _passphrase { new QString("pwd") }; QString* _passphrase { new QString("") };
void updateImageProvider(); void updateImageProvider();
bool encryptFile(const QString& inputFilePath, const QString& outputFilePath); bool encryptFile(const QString& inputFilePath, const QString& outputFilePath);

View file

@ -910,6 +910,8 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
auto& fbxJoints = _animation->getGeometry().joints; auto& fbxJoints = _animation->getGeometry().joints;
auto& originalFbxJoints = _model->getFBXGeometry().joints;
bool allowTranslation = entity->getAnimationAllowTranslation();
int frameCount = frames.size(); int frameCount = frames.size();
if (frameCount <= 0) { if (frameCount <= 0) {
return; return;
@ -952,7 +954,9 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
int index = _jointMapping[j]; int index = _jointMapping[j];
if (index >= 0) { if (index >= 0) {
glm::mat4 translationMat; glm::mat4 translationMat;
if (index < translations.size()) { if (!allowTranslation){
translationMat = glm::translate(originalFbxJoints[index].translation);
} else if (index < translations.size()) {
translationMat = glm::translate(translations[index]); translationMat = glm::translate(translations[index]);
} }
glm::mat4 rotationMat; glm::mat4 rotationMat;

View file

@ -33,6 +33,7 @@ bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b
void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_PLAYING, Animation, animation, Running, running); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_PLAYING, Animation, animation, Running, running);
@ -46,6 +47,7 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire
void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, url, QString, setURL); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, url, QString, setURL);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, allowTranslation, bool, setAllowTranslation);
// legacy property support // legacy property support
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationURL, QString, setURL, getURL); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationURL, QString, setURL, getURL);
@ -67,6 +69,7 @@ void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, boo
void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) { void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) {
COPY_PROPERTY_IF_CHANGED(url); COPY_PROPERTY_IF_CHANGED(url);
COPY_PROPERTY_IF_CHANGED(allowTranslation);
COPY_PROPERTY_IF_CHANGED(fps); COPY_PROPERTY_IF_CHANGED(fps);
COPY_PROPERTY_IF_CHANGED(currentFrame); COPY_PROPERTY_IF_CHANGED(currentFrame);
COPY_PROPERTY_IF_CHANGED(running); COPY_PROPERTY_IF_CHANGED(running);
@ -88,6 +91,7 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
float lastFrame = getLastFrame(); float lastFrame = getLastFrame();
bool loop = getLoop(); bool loop = getLoop();
bool hold = getHold(); bool hold = getHold();
bool allowTranslation = getAllowTranslation();
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
QJsonObject settingsAsJsonObject = settingsAsJson.object(); QJsonObject settingsAsJsonObject = settingsAsJson.object();
@ -122,6 +126,11 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
running = settingsMap["hold"].toBool(); running = settingsMap["hold"].toBool();
} }
if (settingsMap.contains("allowTranslation")) {
allowTranslation = settingsMap["allowTranslation"].toBool();
}
setAllowTranslation(allowTranslation);
setFPS(fps); setFPS(fps);
setCurrentFrame(currentFrame); setCurrentFrame(currentFrame);
setRunning(running); setRunning(running);
@ -137,6 +146,7 @@ void AnimationPropertyGroup::debugDump() const {
qCDebug(entities) << " url:" << getURL() << " has changed:" << urlChanged(); qCDebug(entities) << " url:" << getURL() << " has changed:" << urlChanged();
qCDebug(entities) << " fps:" << getFPS() << " has changed:" << fpsChanged(); qCDebug(entities) << " fps:" << getFPS() << " has changed:" << fpsChanged();
qCDebug(entities) << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged(); qCDebug(entities) << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged();
qCDebug(entities) << "allowTranslation:" << getAllowTranslation() << " has changed:" << allowTranslationChanged();
} }
void AnimationPropertyGroup::listChangedProperties(QList<QString>& out) { void AnimationPropertyGroup::listChangedProperties(QList<QString>& out) {
@ -149,6 +159,9 @@ void AnimationPropertyGroup::listChangedProperties(QList<QString>& out) {
if (currentFrameChanged()) { if (currentFrameChanged()) {
out << "animation-currentFrame"; out << "animation-currentFrame";
} }
if (allowTranslationChanged()) {
out << "animation-allowTranslation";
}
} }
@ -162,6 +175,7 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
bool successPropertyFits = true; bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, getAllowTranslation());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning());
@ -181,6 +195,7 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF
bool somethingChanged = false; bool somethingChanged = false;
READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL);
READ_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, bool, setAllowTranslation);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame);
@ -198,7 +213,8 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation);
processedBytes += bytesRead; processedBytes += bytesRead;
Q_UNUSED(somethingChanged); Q_UNUSED(somethingChanged);
@ -215,6 +231,7 @@ void AnimationPropertyGroup::markAllChanged() {
_firstFrameChanged = true; _firstFrameChanged = true;
_lastFrameChanged = true; _lastFrameChanged = true;
_holdChanged = true; _holdChanged = true;
_allowTranslationChanged = true;
} }
EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const {
@ -228,12 +245,14 @@ EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_ALLOW_TRANSLATION, allowTranslation);
return changedProperties; return changedProperties;
} }
void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) const { void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) const {
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, URL, getURL); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, URL, getURL);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, AllowTranslation, getAllowTranslation);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, getRunning); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, getRunning);
@ -247,6 +266,7 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie
bool somethingChanged = false; bool somethingChanged = false;
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, URL, url, setURL); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, URL, url, setURL);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, AllowTranslation, allowTranslation, setAllowTranslation);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, setRunning); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, setRunning);
@ -268,6 +288,7 @@ EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamP
requestedProperties += PROP_ANIMATION_FIRST_FRAME; requestedProperties += PROP_ANIMATION_FIRST_FRAME;
requestedProperties += PROP_ANIMATION_LAST_FRAME; requestedProperties += PROP_ANIMATION_LAST_FRAME;
requestedProperties += PROP_ANIMATION_HOLD; requestedProperties += PROP_ANIMATION_HOLD;
requestedProperties += PROP_ANIMATION_ALLOW_TRANSLATION;
return requestedProperties; return requestedProperties;
} }
@ -283,6 +304,7 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En
bool successPropertyFits = true; bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, getAllowTranslation());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning());
@ -301,6 +323,7 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char
const unsigned char* dataAt = data; const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL);
READ_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, bool, setAllowTranslation);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame);
READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning); READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning);

View file

@ -85,6 +85,7 @@ public:
DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame
DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame
DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold
DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true);
protected: protected:
friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b);

View file

@ -1033,6 +1033,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame);
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame);
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold);
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color);
ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url); ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url);

View file

@ -40,6 +40,7 @@ enum EntityPropertyList {
PROP_ANIMATION_FPS, PROP_ANIMATION_FPS,
PROP_ANIMATION_FRAME_INDEX, PROP_ANIMATION_FRAME_INDEX,
PROP_ANIMATION_PLAYING, PROP_ANIMATION_PLAYING,
PROP_ANIMATION_ALLOW_TRANSLATION,
// these properties are supported by the EntityItem base class // these properties are supported by the EntityItem base class
PROP_REGISTRATION_POINT, PROP_REGISTRATION_POINT,

View file

@ -330,6 +330,10 @@ void ModelEntityItem::setAnimationSettings(const QString& value) {
setAnimationHold(hold); setAnimationHold(hold);
} }
if (settingsMap.contains("allowTranslation")) {
bool allowTranslation = settingsMap["allowTranslation"].toBool();
setAnimationAllowTranslation(allowTranslation);
}
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE; _dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
} }

View file

@ -83,7 +83,10 @@ public:
void setAnimationCurrentFrame(float value); void setAnimationCurrentFrame(float value);
void setAnimationIsPlaying(bool value); void setAnimationIsPlaying(bool value);
void setAnimationFPS(float value); void setAnimationFPS(float value);
void setAnimationAllowTranslation(bool value) { _animationProperties.setAllowTranslation(value); };
bool getAnimationAllowTranslation() const { return _animationProperties.getAllowTranslation(); };
void setAnimationLoop(bool loop); void setAnimationLoop(bool loop);
bool getAnimationLoop() const; bool getAnimationLoop() const;

View file

@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityEdit: case PacketType::EntityEdit:
case PacketType::EntityData: case PacketType::EntityData:
case PacketType::EntityPhysics: case PacketType::EntityPhysics:
return VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE; return VERSION_ENTITIES_ANIMATION_ALLOW_TRANSLATION_PROPERTIES;
case PacketType::EntityQuery: case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree); return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
case PacketType::AvatarIdentity: case PacketType::AvatarIdentity:

View file

@ -260,6 +260,7 @@ const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69;
const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70; const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70;
const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71; const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71;
const PacketVersion VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE = 72; const PacketVersion VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE = 72;
const PacketVersion VERSION_ENTITIES_ANIMATION_ALLOW_TRANSLATION_PROPERTIES = 73;
enum class EntityQueryPacketVersion: PacketVersion { enum class EntityQueryPacketVersion: PacketVersion {
JSONFilter = 18, JSONFilter = 18,

View file

@ -827,10 +827,10 @@ AnimationDetails::AnimationDetails() :
} }
AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop, AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame) : bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame, bool allowTranslation) :
role(role), url(url), fps(fps), priority(priority), loop(loop), hold(hold), role(role), url(url), fps(fps), priority(priority), loop(loop), hold(hold),
startAutomatically(startAutomatically), firstFrame(firstFrame), lastFrame(lastFrame), startAutomatically(startAutomatically), firstFrame(firstFrame), lastFrame(lastFrame),
running(running), currentFrame(currentFrame) { running(running), currentFrame(currentFrame), allowTranslation(allowTranslation) {
} }
@ -847,6 +847,7 @@ QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const Animatio
obj.setProperty("lastFrame", details.lastFrame); obj.setProperty("lastFrame", details.lastFrame);
obj.setProperty("running", details.running); obj.setProperty("running", details.running);
obj.setProperty("currentFrame", details.currentFrame); obj.setProperty("currentFrame", details.currentFrame);
obj.setProperty("allowTranslation", details.allowTranslation);
return obj; return obj;
} }

View file

@ -197,7 +197,7 @@ class AnimationDetails {
public: public:
AnimationDetails(); AnimationDetails();
AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop, AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame); bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame, bool allowTranslation);
QString role; QString role;
QUrl url; QUrl url;
@ -210,6 +210,7 @@ public:
float lastFrame; float lastFrame;
bool running; bool running;
float currentFrame; float currentFrame;
bool allowTranslation;
}; };
Q_DECLARE_METATYPE(AnimationDetails); Q_DECLARE_METATYPE(AnimationDetails);
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event); QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event);

View file

@ -56,6 +56,7 @@
var isHmdPreviewDisabled = true; var isHmdPreviewDisabled = true;
function fromQml(message) { function fromQml(message) {
switch (message.method) { switch (message.method) {
case 'passphrasePopup_cancelClicked':
case 'walletSetup_cancelClicked': case 'walletSetup_cancelClicked':
case 'needsLogIn_cancelClicked': case 'needsLogIn_cancelClicked':
tablet.gotoHomeScreen(); tablet.gotoHomeScreen();

View file

@ -446,6 +446,10 @@
<input type="checkbox" id="property-model-animation-hold"> <input type="checkbox" id="property-model-animation-hold">
<label for="property-model-animation-hold">Animation hold</label> <label for="property-model-animation-hold">Animation hold</label>
</div> </div>
<div class="property checkbox indent">
<input type="checkbox" id="property-model-animation-allow-translation">
<label for="property-model-animation-allow-translation">Animation Allow Translation</label>
</div>
<div id="animation-fps" class="property number"> <div id="animation-fps" class="property number">
<label>Animation FPS</label> <label>Animation FPS</label>
<input type="number" id="property-model-animation-fps"> <input type="number" id="property-model-animation-fps">
@ -642,10 +646,6 @@
</div> </div>
</fieldset> </fieldset>
<fieldset id="polyvox" class="major"> <fieldset id="polyvox" class="major">
<legend class="section-header spatial-group poly-vox-section property xyz"> <legend class="section-header spatial-group poly-vox-section property xyz">
Voxel volume size <span>m</span> Voxel volume size <span>m</span>

View file

@ -611,6 +611,7 @@ function loaded() {
var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame"); var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame");
var elModelAnimationLoop = document.getElementById("property-model-animation-loop"); var elModelAnimationLoop = document.getElementById("property-model-animation-loop");
var elModelAnimationHold = document.getElementById("property-model-animation-hold"); var elModelAnimationHold = document.getElementById("property-model-animation-hold");
var elModelAnimationAllowTranslation = document.getElementById("property-model-animation-allow-translation");
var elModelTextures = document.getElementById("property-model-textures"); var elModelTextures = document.getElementById("property-model-textures");
var elModelOriginalTextures = document.getElementById("property-model-original-textures"); var elModelOriginalTextures = document.getElementById("property-model-original-textures");
@ -926,6 +927,7 @@ function loaded() {
elModelAnimationLastFrame.value = properties.animation.lastFrame; elModelAnimationLastFrame.value = properties.animation.lastFrame;
elModelAnimationLoop.checked = properties.animation.loop; elModelAnimationLoop.checked = properties.animation.loop;
elModelAnimationHold.checked = properties.animation.hold; elModelAnimationHold.checked = properties.animation.hold;
elModelAnimationAllowTranslation.checked = properties.animation.allowTranslation;
elModelTextures.value = properties.textures; elModelTextures.value = properties.textures;
setTextareaScrolling(elModelTextures); setTextareaScrolling(elModelTextures);
elModelOriginalTextures.value = properties.originalTextures; elModelOriginalTextures.value = properties.originalTextures;
@ -1276,6 +1278,7 @@ function loaded() {
elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame')); elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame'));
elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop')); elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop'));
elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold')); elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold'));
elModelAnimationAllowTranslation.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'allowTranslation'));
elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures')); elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));

View file

@ -27,7 +27,7 @@
var isPreparing = false; // Explicitly track download request status. var isPreparing = false; // Explicitly track download request status.
var confirmAllPurchases = false; // Set this to "true" to cause Checkout.qml to popup for all items, even if free var confirmAllPurchases = false; // Set this to "true" to cause Checkout.qml to popup for all items, even if free
function injectCommonCode(isDirectoryPage) { function injectCommonCode(isDirectoryPage) {
// Supporting styles from marketplaces.css. // Supporting styles from marketplaces.css.
@ -122,7 +122,7 @@
function injectBuyButtonOnMainPage() { function injectBuyButtonOnMainPage() {
var cost; var cost;
$('.grid-item').find('#price-or-edit').find('a').each(function() { $('.grid-item').find('#price-or-edit').find('a').each(function() {
$(this).attr('data-href', $(this).attr('href')); $(this).attr('data-href', $(this).attr('href'));
$(this).attr('href', '#'); $(this).attr('href', '#');
@ -130,7 +130,7 @@
$(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6');
$(this).closest('.col-xs-3').attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').attr("class", 'col-xs-6');
if (parseInt(cost) > 0) { if (parseInt(cost) > 0) {
var priceElement = $(this).find('.price') var priceElement = $(this).find('.price')
priceElement.css({ "width": "auto", "padding": "3px 5px", "height": "26px" }); priceElement.css({ "width": "auto", "padding": "3px 5px", "height": "26px" });
@ -138,8 +138,8 @@
priceElement.css({ "min-width": priceElement.width() + 10 }); priceElement.css({ "min-width": priceElement.width() + 10 });
} }
}); });
$('.grid-item').find('#price-or-edit').find('a').on('click', function () { $('.grid-item').find('#price-or-edit').find('a').on('click', function () {
buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'), buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'),
$(this).closest('.grid-item').find('.item-title').text(), $(this).closest('.grid-item').find('.item-title').text(),
@ -175,11 +175,12 @@
if (confirmAllPurchases) { if (confirmAllPurchases) {
var href = $('#side-info').find('.btn').first().attr('href'); var href = $('#side-info').find('.btn').first().attr('href');
$('#side-info').find('.btn').first().attr('href', '#'); $('#side-info').find('.btn').first().attr('href', '#');
var cost = $('.item-cost').text(); var cost = $('.item-cost').text();
if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) {
$('#side-info').find('.btn').first().html('<span class="glyphicon glyphicon-download" id="buyItemButton"></span>Own Item: ' + (parseFloat(cost / 100).toFixed(2)) + ' HFC'); $('#side-info').find('.btn').first().html('<span class="glyphicon glyphicon-download" id="buyItemButton"></span>Own Item: ' + cost + ' HFC');
} }
$('#side-info').find('.btn').first().on('click', function () { $('#side-info').find('.btn').first().on('click', function () {
@ -264,7 +265,7 @@
// Reference: https://clara.io/learn/sdk/api/export // Reference: https://clara.io/learn/sdk/api/export
//var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true&centerScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; //var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true&centerScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL";
// 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to // 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to
// be successful in generating zip files. // be successful in generating zip files.
var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL"; var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL";
@ -447,7 +448,7 @@
cancelClaraDownload(); cancelClaraDownload();
} else { } else {
var parsedJsonMessage = JSON.parse(message); var parsedJsonMessage = JSON.parse(message);
if (parsedJsonMessage.type === "marketplaces") { if (parsedJsonMessage.type === "marketplaces") {
if (parsedJsonMessage.action === "inspectionModeSetting") { if (parsedJsonMessage.action === "inspectionModeSetting") {
confirmAllPurchases = !!parsedJsonMessage.data; confirmAllPurchases = !!parsedJsonMessage.data;

View file

@ -197,6 +197,7 @@
// Description: // Description:
// -Called when a message is received from Checkout.qml. The "message" argument is what is sent from the Checkout QML // -Called when a message is received from Checkout.qml. The "message" argument is what is sent from the Checkout QML
// in the format "{method, params}", like json-rpc. // in the format "{method, params}", like json-rpc.
var isHmdPreviewDisabled = true;
function fromQml(message) { function fromQml(message) {
switch (message.method) { switch (message.method) {
case 'checkout_setUpClicked': case 'checkout_setUpClicked':
@ -231,12 +232,20 @@
case 'purchases_goToMarketplaceClicked': case 'purchases_goToMarketplaceClicked':
tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
break; break;
case 'passphrasePopup_cancelClicked':
case 'needsLogIn_cancelClicked': case 'needsLogIn_cancelClicked':
tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
break; break;
case 'needsLogIn_loginClicked': case 'needsLogIn_loginClicked':
openLoginWindow(); openLoginWindow();
break; break;
case 'disableHmdPreview':
isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview");
Menu.setIsOptionChecked("Disable Preview", true);
break;
case 'maybeEnableHmdPreview':
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
break;
default: default:
print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message));
} }