Merge branch 'master' into tablet_home_paginate

This commit is contained in:
vladest 2017-12-07 19:16:13 +01:00
commit c9f86e4b6b
48 changed files with 853 additions and 674 deletions

View file

@ -25,6 +25,23 @@ AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) :
_avatar->setID(nodeID); _avatar->setID(nodeID);
} }
uint64_t AvatarMixerClientData::getLastOtherAvatarEncodeTime(QUuid otherAvatar) const {
std::unordered_map<QUuid, uint64_t>::const_iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar);
if (itr != _lastOtherAvatarEncodeTime.end()) {
return itr->second;
}
return 0;
}
void AvatarMixerClientData::setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time) {
std::unordered_map<QUuid, uint64_t>::iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar);
if (itr != _lastOtherAvatarEncodeTime.end()) {
itr->second = time;
} else {
_lastOtherAvatarEncodeTime.emplace(std::pair<QUuid, uint64_t>(otherAvatar, time));
}
}
void AvatarMixerClientData::queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) { void AvatarMixerClientData::queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) {
if (!_packetQueue.node) { if (!_packetQueue.node) {
_packetQueue.node = node; _packetQueue.node = node;

View file

@ -110,16 +110,10 @@ public:
bool getRequestsDomainListData() { return _requestsDomainListData; } bool getRequestsDomainListData() { return _requestsDomainListData; }
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
ViewFrustum getViewFrustom() const { return _currentViewFrustum; } ViewFrustum getViewFrustum() const { return _currentViewFrustum; }
quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
quint64 result = 0; void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time);
if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) {
result = _lastOtherAvatarEncodeTime[otherAvatar];
}
_lastOtherAvatarEncodeTime[otherAvatar] = usecTimestampNow();
return result;
}
QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) { QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) {
_lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount()); _lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount());
@ -143,7 +137,7 @@ private:
// this is a map of the last time we encoded an "other" avatar for // this is a map of the last time we encoded an "other" avatar for
// sending to "this" node // sending to "this" node
std::unordered_map<QUuid, quint64> _lastOtherAvatarEncodeTime; std::unordered_map<QUuid, uint64_t> _lastOtherAvatarEncodeTime;
std::unordered_map<QUuid, QVector<JointData>> _lastOtherAvatarSentJoints; std::unordered_map<QUuid, QVector<JointData>> _lastOtherAvatarSentJoints;
uint64_t _identityChangeTimestamp; uint64_t _identityChangeTimestamp;

View file

@ -22,6 +22,7 @@
#include <NodeList.h> #include <NodeList.h>
#include <Node.h> #include <Node.h>
#include <OctreeConstants.h> #include <OctreeConstants.h>
#include <PrioritySortUtil.h>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <StDev.h> #include <StDev.h>
@ -32,6 +33,10 @@
#include "AvatarMixerClientData.h" #include "AvatarMixerClientData.h"
#include "AvatarMixerSlave.h" #include "AvatarMixerSlave.h"
namespace PrioritySortUtil {
// we declare this callback here but override it later
std::function<uint64_t(const AvatarSharedPointer&)> getAvatarAgeCallback = [] (const AvatarSharedPointer& avatar) { return 0; };
}
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) { void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
_begin = begin; _begin = begin;
@ -184,10 +189,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes
// for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes std::vector<AvatarSharedPointer> avatarsToSort;
QList<AvatarSharedPointer> avatarList;
std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes; std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes;
std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
// make sure this is an agent that we have avatar data for before considering it for inclusion // make sure this is an agent that we have avatar data for before considering it for inclusion
if (otherNode->getType() == NodeType::Agent if (otherNode->getType() == NodeType::Agent
@ -195,36 +198,61 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData()); const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer();
avatarList << otherAvatar; avatarsToSort.push_back(otherAvatar);
avatarDataToNodes[otherAvatar] = otherNode; avatarDataToNodes[otherAvatar] = otherNode;
} }
}); });
AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); // now that we've assembled the avatarDataToNodes map we can replace PrioritySortUtil::getAvatarAgeCallback
ViewFrustum cameraView = nodeData->getViewFrustom(); // with the true implementation
std::priority_queue<AvatarPriority> sortedAvatars; PrioritySortUtil::getAvatarAgeCallback = [&] (const AvatarSharedPointer& avatar) {
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars,
[&](AvatarSharedPointer avatar)->uint64_t {
auto avatarNode = avatarDataToNodes[avatar]; auto avatarNode = avatarDataToNodes[avatar];
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
return nodeData->getLastBroadcastTime(avatarNode->getUUID()); return nodeData->getLastOtherAvatarEncodeTime(avatarNode->getUUID());
}, [&](AvatarSharedPointer avatar)->float{ };
glm::vec3 nodeBoxHalfScale = (avatar->getWorldPosition() - avatar->getGlobalBoundingBoxCorner() * avatar->getSensorToWorldScale());
class SortableAvatar: public PrioritySortUtil::Sortable {
public:
SortableAvatar() = delete;
SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {}
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
float getRadius() const override {
glm::vec3 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale());
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
}, [&](AvatarSharedPointer avatar)->bool { }
uint64_t getTimestamp() const override {
// use the callback implemented above
return PrioritySortUtil::getAvatarAgeCallback(_avatar);
}
const AvatarSharedPointer& getAvatar() const { return _avatar; }
private:
AvatarSharedPointer _avatar;
};
// prepare to sort
ViewFrustum cameraView = nodeData->getViewFrustum();
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
// ignore or sort
const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer();
for (const auto& avatar : avatarsToSort) {
if (avatar == thisAvatar) { if (avatar == thisAvatar) {
return true; // ignore ourselves... // don't echo updates to self
continue;
} }
bool shouldIgnore = false; bool shouldIgnore = false;
// We ignore other nodes for a couple of reasons:
// We will also ignore other nodes for a couple of different reasons:
// 1) ignore bubbles and ignore specific node // 1) ignore bubbles and ignore specific node
// 2) the node hasn't really updated it's frame data recently, this can // 2) the node hasn't really updated it's frame data recently, this can
// happen if for example the avatar is connected on a desktop and sending // happen if for example the avatar is connected on a desktop and sending
// updates at ~30hz. So every 3 frames we skip a frame. // updates at ~30hz. So every 3 frames we skip a frame.
auto avatarNode = avatarDataToNodes[avatar];
auto avatarNode = avatarDataToNodes[avatar];
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData()); const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
@ -240,7 +268,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|| (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) {
shouldIgnore = true; shouldIgnore = true;
} else { } else {
// Check to see if the space bubble is enabled // Check to see if the space bubble is enabled
// Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored
if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
@ -267,8 +294,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID());
} }
} }
quint64 endIgnoreCalculation = usecTimestampNow();
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
if (!shouldIgnore) { if (!shouldIgnore) {
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID());
@ -292,20 +317,21 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
++numAvatarsWithSkippedFrames; ++numAvatarsWithSkippedFrames;
} }
} }
return shouldIgnore; quint64 endIgnoreCalculation = usecTimestampNow();
}); _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
if (!shouldIgnore) {
// sort this one for later
sortedAvatars.push(SortableAvatar(avatar));
}
}
// loop through our sorted avatars and allocate our bandwidth to them accordingly // loop through our sorted avatars and allocate our bandwidth to them accordingly
int avatarRank = 0;
// this is overly conservative, because it includes some avatars we might not consider
int remainingAvatars = (int)sortedAvatars.size(); int remainingAvatars = (int)sortedAvatars.size();
while (!sortedAvatars.empty()) { while (!sortedAvatars.empty()) {
AvatarPriority sortData = sortedAvatars.top(); const auto& avatarData = sortedAvatars.top().getAvatar();
sortedAvatars.pop(); sortedAvatars.pop();
const auto& avatarData = sortData.avatar;
avatarRank++;
remainingAvatars--; remainingAvatars--;
auto otherNode = avatarDataToNodes[avatarData]; auto otherNode = avatarDataToNodes[avatarData];
@ -332,10 +358,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow()); nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
} }
// determine if avatar is in view which determines how much data to send
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
// determine if avatar is in view, to determine how much data to include...
glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale(); glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale();
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
bool isInView = nodeData->otherAvatarInView(otherNodeBox); bool isInView = nodeData->otherAvatarInView(otherNodeBox);
@ -405,14 +429,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// set the last sent sequence number for this sender on the receiver // set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNodeData->getLastReceivedSequenceNumber()); otherNodeData->getLastReceivedSequenceNumber());
nodeData->setLastOtherAvatarEncodeTime(otherNode->getUUID(), usecTimestampNow());
} }
} else {
// TODO? this avatar is not included now, and will probably not be included next frame.
// It would be nice if we could tweak its future sort priority to put it at the back of the list.
} }
avatarPacketList->endSegment(); avatarPacketList->endSegment();
quint64 endAvatarDataPacking = usecTimestampNow(); quint64 endAvatarDataPacking = usecTimestampNow();
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
}; }
quint64 startPacketSending = usecTimestampNow(); quint64 startPacketSending = usecTimestampNow();

View file

@ -29,10 +29,6 @@ macro(GENERATE_INSTALLERS)
if (WIN32) if (WIN32)
# Do not install the Visual Studio C runtime libraries. The installer will do this automatically
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries)
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico") set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
# install and reference the Add/Remove icon # install and reference the Add/Remove icon
@ -49,6 +45,10 @@ macro(GENERATE_INSTALLERS)
set(_UNINSTALLER_HEADER_BAD_PATH "${HF_CMAKE_DIR}/installer/uninstaller-header.bmp") set(_UNINSTALLER_HEADER_BAD_PATH "${HF_CMAKE_DIR}/installer/uninstaller-header.bmp")
set(UNINSTALLER_HEADER_IMAGE "") set(UNINSTALLER_HEADER_IMAGE "")
fix_path_for_nsis(${_UNINSTALLER_HEADER_BAD_PATH} UNINSTALLER_HEADER_IMAGE) fix_path_for_nsis(${_UNINSTALLER_HEADER_BAD_PATH} UNINSTALLER_HEADER_IMAGE)
# grab the latest VC redist (2017) and add it to the installer, our NSIS template
# will call it during the install
install(CODE "file(DOWNLOAD https://go.microsoft.com/fwlink/?LinkId=746572 \"\${CMAKE_INSTALL_PREFIX}/vcredist_x64.exe\")")
elseif (APPLE) elseif (APPLE)
# produce a drag and drop DMG on OS X # produce a drag and drop DMG on OS X
set(CPACK_GENERATOR "DragNDrop") set(CPACK_GENERATOR "DragNDrop")
@ -84,4 +84,3 @@ macro(GENERATE_INSTALLERS)
include(CPack) include(CPack)
endmacro() endmacro()

View file

@ -45,5 +45,4 @@ else()
endif() endif()
file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}") file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@") fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@" IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe")

View file

@ -65,21 +65,23 @@ var EventBridge;
// we need to listen to events that might precede the addition of this elements. // we need to listen to events that might precede the addition of this elements.
// A more robust hack will be to add a setInterval that look for DOM changes every 100-300 ms (low performance?) // A more robust hack will be to add a setInterval that look for DOM changes every 100-300 ms (low performance?)
window.onload = function(){ window.addEventListener("load",function(event) {
setTimeout(function() { setTimeout(function() {
EventBridge.forceHtmlAudioOutputDeviceUpdate(); EventBridge.forceHtmlAudioOutputDeviceUpdate();
}, 1200); }, 1200);
}; }, false);
document.onclick = function(){
document.addEventListener("click",function(){
setTimeout(function() { setTimeout(function() {
EventBridge.forceHtmlAudioOutputDeviceUpdate(); EventBridge.forceHtmlAudioOutputDeviceUpdate();
}, 1200); }, 1200);
}; }, false);
document.onchange = function(){
document.addEventListener("change",function(){
setTimeout(function() { setTimeout(function() {
EventBridge.forceHtmlAudioOutputDeviceUpdate(); EventBridge.forceHtmlAudioOutputDeviceUpdate();
}, 1200); }, 1200);
}; }, false);
tempEventBridge._callbacks.forEach(function (callback) { tempEventBridge._callbacks.forEach(function (callback) {
EventBridge.scriptEventReceived.connect(callback); EventBridge.scriptEventReceived.connect(callback);

View file

@ -12,9 +12,9 @@
var MAX_WARNINGS = 3; var MAX_WARNINGS = 3;
var numWarnings = 0; var numWarnings = 0;
var isWindowFocused = true; var isWindowFocused = true;
var isKeyboardRaised = false; window.isKeyboardRaised = false;
var isNumericKeyboard = false; window.isNumericKeyboard = false;
var isPasswordField = false; window.isPasswordField = false;
function shouldSetPasswordField() { function shouldSetPasswordField() {
var nodeType = document.activeElement.type; var nodeType = document.activeElement.type;
@ -62,7 +62,7 @@
var passwordField = shouldSetPasswordField(); var passwordField = shouldSetPasswordField();
if (isWindowFocused && if (isWindowFocused &&
(keyboardRaised !== isKeyboardRaised || numericKeyboard !== isNumericKeyboard || passwordField !== isPasswordField)) { (keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard || passwordField !== window.isPasswordField)) {
if (typeof EventBridge !== "undefined" && EventBridge !== null) { if (typeof EventBridge !== "undefined" && EventBridge !== null) {
EventBridge.emitWebEvent( EventBridge.emitWebEvent(
@ -75,20 +75,20 @@
} }
} }
if (!isKeyboardRaised) { if (!window.isKeyboardRaised) {
scheduleBringToView(250); // Allow time for keyboard to be raised in QML. scheduleBringToView(250); // Allow time for keyboard to be raised in QML.
// 2DO: should it be rather done from 'client area height changed' event? // 2DO: should it be rather done from 'client area height changed' event?
} }
isKeyboardRaised = keyboardRaised; window.isKeyboardRaised = keyboardRaised;
isNumericKeyboard = numericKeyboard; window.isNumericKeyboard = numericKeyboard;
isPasswordField = passwordField; window.isPasswordField = passwordField;
} }
}, POLL_FREQUENCY); }, POLL_FREQUENCY);
window.addEventListener("click", function () { window.addEventListener("click", function () {
var keyboardRaised = shouldRaiseKeyboard(); var keyboardRaised = shouldRaiseKeyboard();
if(keyboardRaised && isKeyboardRaised) { if (keyboardRaised && window.isKeyboardRaised) {
scheduleBringToView(150); scheduleBringToView(150);
} }
}); });
@ -99,7 +99,7 @@
window.addEventListener("blur", function () { window.addEventListener("blur", function () {
isWindowFocused = false; isWindowFocused = false;
isKeyboardRaised = false; window.isKeyboardRaised = false;
isNumericKeyboard = false; window.isNumericKeyboard = false;
}); });
})(); })();

View file

@ -79,10 +79,12 @@ Rectangle {
if (result.status !== 'success') { if (result.status !== 'success') {
failureErrorText.text = result.message; failureErrorText.text = result.message;
root.activeView = "checkoutFailure"; root.activeView = "checkoutFailure";
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemPrice, !root.alreadyOwned, result.message);
} else { } else {
root.itemHref = result.data.download_url; root.itemHref = result.data.download_url;
root.isWearable = result.data.categories.indexOf("Wearables") > -1; root.isWearable = result.data.categories.indexOf("Wearables") > -1;
root.activeView = "checkoutSuccess"; root.activeView = "checkoutSuccess";
UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemPrice, !root.alreadyOwned);
} }
} }
@ -599,6 +601,7 @@ Rectangle {
sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true; rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start(); rezzedNotifContainerTimer.start();
UserActivityLogger.commerceEntityRezzed(root.itemId, "checkout", root.isWearable ? "rez" : "wear");
} }
} }
RalewaySemiBold { RalewaySemiBold {

View file

@ -349,6 +349,7 @@ Item {
sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true; rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start(); rezzedNotifContainerTimer.start();
UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.isWearable ? "rez" : "wear");
} }
style: ButtonStyle { style: ButtonStyle {

View file

@ -206,16 +206,6 @@ Item {
root.isPasswordField = (focus && passphraseField.echoMode === TextInput.Password); root.isPasswordField = (focus && passphraseField.echoMode === TextInput.Password);
} }
MouseArea {
anchors.fill: parent;
onClicked: {
root.keyboardRaised = true;
root.isPasswordField = (passphraseField.echoMode === TextInput.Password);
mouse.accepted = false;
}
}
onAccepted: { onAccepted: {
submitPassphraseInputButton.enabled = false; submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text); commerce.setPassphrase(passphraseField.text);
@ -362,25 +352,6 @@ Item {
right: parent.right; right: parent.right;
} }
Image {
id: lowerKeyboardButton;
z: 999;
source: "images/lowerKeyboard.png";
anchors.right: keyboard.right;
anchors.top: keyboard.showMirrorText ? keyboard.top : undefined;
anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom;
height: 50;
width: 60;
MouseArea {
anchors.fill: parent;
onClicked: {
root.keyboardRaised = false;
}
}
}
HifiControlsUit.Keyboard { HifiControlsUit.Keyboard {
id: keyboard; id: keyboard;
raised: HMD.mounted && root.keyboardRaised; raised: HMD.mounted && root.keyboardRaised;

View file

@ -82,17 +82,6 @@ Item {
if (focus) { if (focus) {
var hidePassword = (currentPassphraseField.echoMode === TextInput.Password); var hidePassword = (currentPassphraseField.echoMode === TextInput.Password);
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
} else if (!passphraseFieldAgain.focus) {
sendSignalToWallet({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
}
}
MouseArea {
anchors.fill: parent;
onPressed: {
var hidePassword = (currentPassphraseField.echoMode === TextInput.Password);
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
mouse.accepted = false;
} }
} }
@ -115,21 +104,10 @@ Item {
activeFocusOnPress: true; activeFocusOnPress: true;
activeFocusOnTab: true; activeFocusOnTab: true;
MouseArea {
anchors.fill: parent;
onPressed: {
var hidePassword = (passphraseField.echoMode === TextInput.Password);
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
mouse.accepted = false;
}
}
onFocusChanged: { onFocusChanged: {
if (focus) { if (focus) {
var hidePassword = (passphraseField.echoMode === TextInput.Password); var hidePassword = (passphraseField.echoMode === TextInput.Password);
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
} else if (!passphraseFieldAgain.focus) {
sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
} }
} }
@ -151,21 +129,10 @@ Item {
activeFocusOnPress: true; activeFocusOnPress: true;
activeFocusOnTab: true; activeFocusOnTab: true;
MouseArea {
anchors.fill: parent;
onPressed: {
var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password);
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
mouse.accepted = false;
}
}
onFocusChanged: { onFocusChanged: {
if (focus) { if (focus) {
var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password); var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password);
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
} else if (!passphraseField.focus) {
sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
} }
} }

View file

@ -47,6 +47,12 @@ Rectangle {
} else if (walletStatus === 1) { } else if (walletStatus === 1) {
if (root.activeView !== "walletSetup") { if (root.activeView !== "walletSetup") {
root.activeView = "walletSetup"; root.activeView = "walletSetup";
commerce.resetLocalWalletOnly();
var timestamp = new Date();
walletSetup.startingTimestamp = timestamp;
walletSetup.setupAttemptID = generateUUID();
UserActivityLogger.commerceWalletSetupStarted(timestamp, setupAttemptID, walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app",
(AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : ''));
} }
} else if (walletStatus === 2) { } else if (walletStatus === 2) {
if (root.activeView !== "passphraseModal") { if (root.activeView !== "passphraseModal") {
@ -172,7 +178,7 @@ Rectangle {
Connections { Connections {
onSendSignalToWallet: { onSendSignalToWallet: {
if (msg.method === 'walletSetup_finished') { if (msg.method === 'walletSetup_finished') {
if (msg.referrer === '') { if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
root.activeView = "initialize"; root.activeView = "initialize";
commerce.getWalletStatus(); commerce.getWalletStatus();
} else if (msg.referrer === 'purchases') { } else if (msg.referrer === 'purchases') {
@ -666,25 +672,6 @@ Rectangle {
right: parent.right; right: parent.right;
} }
Image {
id: lowerKeyboardButton;
z: 999;
source: "images/lowerKeyboard.png";
anchors.right: keyboard.right;
anchors.top: keyboard.showMirrorText ? keyboard.top : undefined;
anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom;
height: 50;
width: 60;
MouseArea {
anchors.fill: parent;
onClicked: {
root.keyboardRaised = false;
}
}
}
HifiControlsUit.Keyboard { HifiControlsUit.Keyboard {
id: keyboard; id: keyboard;
raised: HMD.mounted && root.keyboardRaised; raised: HMD.mounted && root.keyboardRaised;
@ -719,12 +706,28 @@ Rectangle {
case 'updateWalletReferrer': case 'updateWalletReferrer':
walletSetup.referrer = message.referrer; walletSetup.referrer = message.referrer;
break; break;
case 'inspectionCertificate_resetCert':
// NOP
break;
default: default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
} }
} }
signal sendToScript(var message); signal sendToScript(var message);
// generateUUID() taken from:
// https://stackoverflow.com/a/8809472
function generateUUID() { // Public Domain/MIT
var d = new Date().getTime();
if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
d += performance.now(); //use high-precision timer if available
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
// //
// FUNCTION DEFINITIONS END // FUNCTION DEFINITIONS END
// //

View file

@ -68,6 +68,7 @@ Item {
Connections { Connections {
target: GlobalServices target: GlobalServices
onMyUsernameChanged: { onMyUsernameChanged: {
transactionHistoryModel.clear();
usernameText.text = Account.username; usernameText.text = Account.username;
} }
} }

View file

@ -31,6 +31,10 @@ Item {
property bool hasShownSecurityImageTip: false; property bool hasShownSecurityImageTip: false;
property string referrer; property string referrer;
property string keyFilePath; property string keyFilePath;
property date startingTimestamp;
property string setupAttemptID;
readonly property int setupFlowVersion: 1;
readonly property var setupStepNames: [ "Setup Prompt", "Security Image Selection", "Passphrase Selection", "Private Keys Ready" ];
Image { Image {
anchors.fill: parent; anchors.fill: parent;
@ -67,6 +71,13 @@ Item {
anchors.fill: parent; anchors.fill: parent;
} }
onActiveViewChanged: {
var timestamp = new Date();
var currentStepNumber = root.activeView.substring(5);
UserActivityLogger.commerceWalletSetupProgress(timestamp, root.setupAttemptID,
Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]);
}
// //
// TITLE BAR START // TITLE BAR START
// //
@ -371,7 +382,7 @@ Item {
Item { Item {
id: securityImageTip; id: securityImageTip;
visible: false; visible: !root.hasShownSecurityImageTip && root.activeView === "step_3";
z: 999; z: 999;
anchors.fill: root; anchors.fill: root;
@ -421,7 +432,6 @@ Item {
text: "Got It"; text: "Got It";
onClicked: { onClicked: {
root.hasShownSecurityImageTip = true; root.hasShownSecurityImageTip = true;
securityImageTip.visible = false;
passphraseSelection.focusFirstTextField(); passphraseSelection.focusFirstTextField();
} }
} }
@ -439,9 +449,6 @@ Item {
onVisibleChanged: { onVisibleChanged: {
if (visible) { if (visible) {
commerce.getWalletAuthenticatedStatus(); commerce.getWalletAuthenticatedStatus();
if (!root.hasShownSecurityImageTip) {
securityImageTip.visible = true;
}
} }
} }
@ -732,7 +739,11 @@ Item {
text: "Finish"; text: "Finish";
onClicked: { onClicked: {
root.visible = false; root.visible = false;
root.hasShownSecurityImageTip = false;
sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""}); sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""});
var timestamp = new Date();
UserActivityLogger.commerceWalletSetupFinished(timestamp, setupAttemptID, Math.round((timestamp - root.startingTimestamp)/1000));
} }
} }
} }

View file

@ -9,33 +9,30 @@ Overlay {
Image { Image {
id: image id: image
property bool scaleFix: true; property bool scaleFix: true
property real xOffset: 0 property real xStart: 0
property real yOffset: 0 property real yStart: 0
property real xSize: 0
property real ySize: 0
property real imageScale: 1.0 property real imageScale: 1.0
property var resizer: Timer { property var resizer: Timer {
interval: 50 interval: 50
repeat: false repeat: false
running: false running: false
onTriggered: { onTriggered: {
var targetAspect = root.width / root.height; if (image.xSize === 0) {
var sourceAspect = image.sourceSize.width / image.sourceSize.height; image.xSize = image.sourceSize.width - image.xStart;
if (sourceAspect <= targetAspect) {
if (root.width === image.sourceSize.width) {
return;
} }
image.imageScale = root.width / image.sourceSize.width; if (image.ySize === 0) {
} else if (sourceAspect > targetAspect){ image.ySize = image.sourceSize.height - image.yStart;
if (root.height === image.sourceSize.height) {
return;
} }
image.imageScale = root.height / image.sourceSize.height;
} image.anchors.leftMargin = -image.xStart * root.width / image.xSize;
image.sourceSize = Qt.size(image.sourceSize.width * image.imageScale, image.sourceSize.height * image.imageScale); image.anchors.topMargin = -image.yStart * root.height / image.ySize;
image.anchors.rightMargin = (image.xStart + image.xSize - image.sourceSize.width) * root.width / image.xSize;
image.anchors.bottomMargin = (image.yStart + image.ySize - image.sourceSize.height) * root.height / image.ySize;
} }
} }
x: -1 * xOffset * imageScale
y: -1 * yOffset * imageScale
onSourceSizeChanged: { onSourceSizeChanged: {
if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) { if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) {
@ -43,6 +40,8 @@ Overlay {
resizer.start(); resizer.start();
} }
} }
anchors.fill: parent
} }
ColorOverlay { ColorOverlay {
@ -57,8 +56,10 @@ Overlay {
var key = keys[i]; var key = keys[i];
var value = subImage[key]; var value = subImage[key];
switch (key) { switch (key) {
case "x": image.xOffset = value; break; case "x": image.xStart = value; break;
case "y": image.yOffset = value; break; case "y": image.yStart = value; break;
case "width": image.xSize = value; break;
case "height": image.ySize = value; break;
} }
} }
} }

View file

@ -7075,11 +7075,11 @@ QRect Application::getRecommendedHUDRect() const {
return result; return result;
} }
QSize Application::getDeviceSize() const { glm::vec2 Application::getDeviceSize() const {
static const int MIN_SIZE = 1; static const int MIN_SIZE = 1;
QSize result(MIN_SIZE, MIN_SIZE); glm::vec2 result(MIN_SIZE);
if (_displayPlugin) { if (_displayPlugin) {
result = fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize()); result = getActiveDisplayPlugin()->getRecommendedRenderSize();
} }
return result; return result;
} }
@ -7098,10 +7098,6 @@ bool Application::hasFocus() const {
return (QApplication::activeWindow() != nullptr); return (QApplication::activeWindow() != nullptr);
} }
glm::vec2 Application::getViewportDimensions() const {
return toGlm(getDeviceSize());
}
void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) { void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) {
if (maxOctreePPS != _maxOctreePPS) { if (maxOctreePPS != _maxOctreePPS) {
_maxOctreePPS = maxOctreePPS; _maxOctreePPS = maxOctreePPS;

View file

@ -158,7 +158,7 @@ public:
glm::uvec2 getUiSize() const; glm::uvec2 getUiSize() const;
QRect getRecommendedHUDRect() const; QRect getRecommendedHUDRect() const;
QSize getDeviceSize() const; glm::vec2 getDeviceSize() const;
bool hasFocus() const; bool hasFocus() const;
void showCursor(const Cursor::Icon& cursor); void showCursor(const Cursor::Icon& cursor);
@ -228,8 +228,6 @@ public:
FileLogger* getLogger() const { return _logger; } FileLogger* getLogger() const { return _logger; }
glm::vec2 getViewportDimensions() const;
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; } NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
float getRenderResolutionScale() const; float getRenderResolutionScale() const;

View file

@ -104,8 +104,7 @@ void Application::paintGL() {
PerformanceTimer perfTimer("renderOverlay"); PerformanceTimer perfTimer("renderOverlay");
// NOTE: There is no batch associated with this renderArgs // NOTE: There is no batch associated with this renderArgs
// the ApplicationOverlay class assumes it's viewport is setup to be the device size // the ApplicationOverlay class assumes it's viewport is setup to be the device size
QSize size = getDeviceSize(); renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize());
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
_applicationOverlay.renderOverlay(&renderArgs); _applicationOverlay.renderOverlay(&renderArgs);
} }

View file

@ -28,6 +28,7 @@
#include <shared/QtHelpers.h> #include <shared/QtHelpers.h>
#include <AvatarData.h> #include <AvatarData.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <PrioritySortUtil.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include <Rig.h> #include <Rig.h>
#include <SettingHandle.h> #include <SettingHandle.h>
@ -142,32 +143,39 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
PerformanceTimer perfTimer("otherAvatars"); PerformanceTimer perfTimer("otherAvatars");
auto avatarMap = getHashCopy(); class SortableAvatar: public PrioritySortUtil::Sortable {
QList<AvatarSharedPointer> avatarList = avatarMap.values(); public:
SortableAvatar() = delete;
SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {}
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
float getRadius() const override { return std::static_pointer_cast<Avatar>(_avatar)->getBoundingRadius(); }
uint64_t getTimestamp() const override { return std::static_pointer_cast<Avatar>(_avatar)->getLastRenderUpdateTime(); }
const AvatarSharedPointer& getAvatar() const { return _avatar; }
private:
AvatarSharedPointer _avatar;
};
ViewFrustum cameraView; ViewFrustum cameraView;
qApp->copyDisplayViewFrustum(cameraView); qApp->copyDisplayViewFrustum(cameraView);
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
std::priority_queue<AvatarPriority> sortedAvatars; // sort
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, auto avatarMap = getHashCopy();
AvatarHash::iterator itr = avatarMap.begin();
[](AvatarSharedPointer avatar)->uint64_t{ while (itr != avatarMap.end()) {
return std::static_pointer_cast<Avatar>(avatar)->getLastRenderUpdateTime(); const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
},
[](AvatarSharedPointer avatar)->float{
return std::static_pointer_cast<Avatar>(avatar)->getBoundingRadius();
},
[this](AvatarSharedPointer avatar)->bool{
const auto& castedAvatar = std::static_pointer_cast<Avatar>(avatar);
if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) {
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop. // DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
// DO NOT update or fade out uninitialized Avatars // DO NOT update or fade out uninitialized Avatars
return true; // ignore it if (avatar != _myAvatar && avatar->isInitialized()) {
sortedAvatars.push(SortableAvatar(avatar));
}
++itr;
} }
return false;
});
// process in sorted order
uint64_t startTime = usecTimestampNow(); uint64_t startTime = usecTimestampNow();
const uint64_t UPDATE_BUDGET = 2000; // usec const uint64_t UPDATE_BUDGET = 2000; // usec
uint64_t updateExpiry = startTime + UPDATE_BUDGET; uint64_t updateExpiry = startTime + UPDATE_BUDGET;
@ -176,8 +184,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
render::Transaction transaction; render::Transaction transaction;
while (!sortedAvatars.empty()) { while (!sortedAvatars.empty()) {
const AvatarPriority& sortData = sortedAvatars.top(); const SortableAvatar& sortData = sortedAvatars.top();
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar); const auto& avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID()); bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
if (ignoring) { if (ignoring) {
@ -207,7 +215,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
uint64_t now = usecTimestampNow(); uint64_t now = usecTimestampNow();
if (now < updateExpiry) { if (now < updateExpiry) {
// we're within budget // we're within budget
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (inView && avatar->hasNewJointData()) { if (inView && avatar->hasNewJointData()) {
numAvatarsUpdated++; numAvatarsUpdated++;
} }
@ -221,7 +229,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
// --> some avatar velocity measurements may be a little off // --> some avatar velocity measurements may be a little off
// no time simulate, but we take the time to count how many were tragically missed // no time simulate, but we take the time to count how many were tragically missed
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (!inView) { if (!inView) {
break; break;
} }
@ -230,9 +238,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
} }
sortedAvatars.pop(); sortedAvatars.pop();
while (inView && !sortedAvatars.empty()) { while (inView && !sortedAvatars.empty()) {
const AvatarPriority& newSortData = sortedAvatars.top(); const SortableAvatar& newSortData = sortedAvatars.top();
const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.avatar); const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
inView = newSortData.priority > OUT_OF_VIEW_THRESHOLD; inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (inView && newAvatar->hasNewJointData()) { if (inView && newAvatar->hasNewJointData()) {
numAVatarsNotUpdated++; numAVatarsNotUpdated++;
} }

View file

@ -128,6 +128,11 @@ void QmlCommerce::reset() {
wallet->reset(); wallet->reset();
} }
void QmlCommerce::resetLocalWalletOnly() {
auto wallet = DependencyManager::get<Wallet>();
wallet->reset();
}
void QmlCommerce::account() { void QmlCommerce::account() {
auto ledger = DependencyManager::get<Ledger>(); auto ledger = DependencyManager::get<Ledger>();
ledger->account(); ledger->account();

View file

@ -65,6 +65,7 @@ protected:
Q_INVOKABLE void history(); Q_INVOKABLE void history();
Q_INVOKABLE void generateKeyPair(); Q_INVOKABLE void generateKeyPair();
Q_INVOKABLE void reset(); Q_INVOKABLE void reset();
Q_INVOKABLE void resetLocalWalletOnly();
Q_INVOKABLE void account(); Q_INVOKABLE void account();
Q_INVOKABLE void certificateInfo(const QString& certificateId); Q_INVOKABLE void certificateInfo(const QString& certificateId);

View file

@ -321,6 +321,16 @@ Wallet::Wallet() {
auto accountManager = DependencyManager::get<AccountManager>(); auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
getWalletStatus(); getWalletStatus();
_publicKeys.clear();
if (_securityImage) {
delete _securityImage;
}
_securityImage = nullptr;
// tell the provider we got nothing
updateImageProvider();
_passphrase->clear();
}); });
} }

View file

@ -176,6 +176,10 @@ bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
return offscreenUi->isPointOnDesktopWindow(point); return offscreenUi->isPointOnDesktopWindow(point);
} }
glm::vec2 WindowScriptingInterface::getDeviceSize() const {
return qApp->getDeviceSize();
}
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and /// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
/// might be in same thread as a script that sets the reticle to invisible /// might be in same thread as a script that sets the reticle to invisible
void WindowScriptingInterface::ensureReticleVisible() const { void WindowScriptingInterface::ensureReticleVisible() const {

View file

@ -12,6 +12,8 @@
#ifndef hifi_WindowScriptingInterface_h #ifndef hifi_WindowScriptingInterface_h
#define hifi_WindowScriptingInterface_h #define hifi_WindowScriptingInterface_h
#include <glm/glm.hpp>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtQuick/QQuickItem> #include <QtQuick/QQuickItem>
@ -73,6 +75,7 @@ public slots:
bool isPhysicsEnabled(); bool isPhysicsEnabled();
bool setDisplayTexture(const QString& name); bool setDisplayTexture(const QString& name);
bool isPointOnDesktopWindow(QVariant point); bool isPointOnDesktopWindow(QVariant point);
glm::vec2 getDeviceSize() const;
int openMessageBox(QString title, QString text, int buttons, int defaultButton); int openMessageBox(QString title, QString text, int buttons, int defaultButton);
void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton); void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton);

View file

@ -35,7 +35,8 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
_modelTextures(QVariantMap()), _modelTextures(QVariantMap()),
_url(modelOverlay->_url), _url(modelOverlay->_url),
_updateModel(false), _updateModel(false),
_loadPriority(modelOverlay->getLoadPriority()) _scaleToFit(modelOverlay->_scaleToFit),
_loadPriority(modelOverlay->_loadPriority)
{ {
_model->init(); _model->init();
_model->setLoadingPriority(_loadPriority); _model->setLoadingPriority(_loadPriority);

View file

@ -75,8 +75,8 @@ private:
QVariantMap _modelTextures; QVariantMap _modelTextures;
QUrl _url; QUrl _url;
bool _updateModel = { false }; bool _updateModel { false };
bool _scaleToFit = { false }; bool _scaleToFit { false };
float _loadPriority { 0.0f }; float _loadPriority { 0.0f };
AnimationPointer _animation; AnimationPointer _animation;
@ -87,7 +87,7 @@ private:
bool _animationRunning { false }; bool _animationRunning { false };
bool _animationLoop { false }; bool _animationLoop { false };
float _animationFirstFrame { 0.0f }; float _animationFirstFrame { 0.0f };
float _animationLastFrame = { 0.0f }; float _animationLastFrame { 0.0f };
bool _animationHold { false }; bool _animationHold { false };
bool _animationAllowTranslation { false }; bool _animationAllowTranslation { false };
uint64_t _lastAnimated { 0 }; uint64_t _lastAnimated { 0 };

View file

@ -18,8 +18,9 @@
QString const Shape3DOverlay::TYPE = "shape"; QString const Shape3DOverlay::TYPE = "shape";
Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) : Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* shape3DOverlay) :
Volume3DOverlay(Shape3DOverlay) Volume3DOverlay(shape3DOverlay),
_shape(shape3DOverlay->_shape)
{ {
} }

View file

@ -23,7 +23,7 @@ public:
virtual QString getType() const override { return TYPE; } virtual QString getType() const override { return TYPE; }
Shape3DOverlay() {} Shape3DOverlay() {}
Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay); Shape3DOverlay(const Shape3DOverlay* shape3DOverlay);
virtual void render(RenderArgs* args) override; virtual void render(RenderArgs* args) override;
virtual const render::ShapeKey getShapeKey() override; virtual const render::ShapeKey getShapeKey() override;

View file

@ -14,7 +14,8 @@
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) : Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) :
Base3DOverlay(volume3DOverlay) Base3DOverlay(volume3DOverlay),
_localBoundingBox(volume3DOverlay->_localBoundingBox)
{ {
} }

View file

@ -2387,63 +2387,10 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f; const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f;
float AvatarData::_avatarSortCoefficientSize { 0.5f }; float AvatarData::_avatarSortCoefficientSize { 1.0f };
float AvatarData::_avatarSortCoefficientCenter { 0.25 }; float AvatarData::_avatarSortCoefficientCenter { 0.25 };
float AvatarData::_avatarSortCoefficientAge { 1.0f }; float AvatarData::_avatarSortCoefficientAge { 1.0f };
void AvatarData::sortAvatars(
QList<AvatarSharedPointer> avatarList,
const ViewFrustum& cameraView,
std::priority_queue<AvatarPriority>& sortedAvatarsOut,
std::function<uint64_t(AvatarSharedPointer)> getLastUpdated,
std::function<float(AvatarSharedPointer)> getBoundingRadius,
std::function<bool(AvatarSharedPointer)> shouldIgnore) {
PROFILE_RANGE(simulation, "sort");
uint64_t now = usecTimestampNow();
glm::vec3 frustumCenter = cameraView.getPosition();
const glm::vec3& forward = cameraView.getDirection();
for (int32_t i = 0; i < avatarList.size(); ++i) {
const auto& avatar = avatarList.at(i);
if (shouldIgnore(avatar)) {
continue;
}
// priority = weighted linear combination of:
// (a) apparentSize
// (b) proximity to center of view
// (c) time since last update
glm::vec3 avatarPosition = avatar->getWorldPosition();
glm::vec3 offset = avatarPosition - frustumCenter;
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
// FIXME - AvatarData has something equivolent to this
float radius = getBoundingRadius(avatar);
float apparentSize = 2.0f * radius / distance;
float cosineAngle = glm::dot(offset, forward) / distance;
float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND);
// NOTE: we are adding values of different units to get a single measure of "priority".
// Thus we multiply each component by a conversion "weight" that scales its units relative to the others.
// These weights are pure magic tuning and should be hard coded in the relation below,
// but are currently exposed for anyone who would like to explore fine tuning:
float priority = _avatarSortCoefficientSize * apparentSize
+ _avatarSortCoefficientCenter * cosineAngle
+ _avatarSortCoefficientAge * age;
// decrement priority of avatars outside keyhole
if (distance > cameraView.getCenterRadius()) {
if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) {
priority += OUT_OF_VIEW_PENALTY;
}
}
sortedAvatarsOut.push(AvatarPriority(avatar, priority));
}
}
QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) {
QScriptValue obj = engine->newObject(); QScriptValue obj = engine->newObject();
for (auto entityID : value.keys()) { for (auto entityID : value.keys()) {

View file

@ -629,14 +629,6 @@ public:
static const float OUT_OF_VIEW_PENALTY; static const float OUT_OF_VIEW_PENALTY;
static void sortAvatars(
QList<AvatarSharedPointer> avatarList,
const ViewFrustum& cameraView,
std::priority_queue<AvatarPriority>& sortedAvatarsOut,
std::function<uint64_t(AvatarSharedPointer)> getLastUpdated,
std::function<float(AvatarSharedPointer)> getBoundingRadius,
std::function<bool(AvatarSharedPointer)> shouldIgnore);
// TODO: remove this HACK once we settle on optimal sort coefficients // TODO: remove this HACK once we settle on optimal sort coefficients
// These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline.
static float _avatarSortCoefficientSize; static float _avatarSortCoefficientSize;

View file

@ -1006,15 +1006,12 @@ NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl)
if (!_spectatorCameraNetworkTexture) { if (!_spectatorCameraNetworkTexture) {
_spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); _spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl));
} }
if (_spectatorCameraFramebuffer) { if (!_spectatorCameraFramebuffer) {
texture = _spectatorCameraFramebuffer->getRenderBuffer(0); getSpectatorCameraFramebuffer(); // initialize frame buffer
if (texture) { }
texture->setSource(SPECTATOR_CAMERA_FRAME_URL.toString().toStdString()); updateSpectatorCameraNetworkTexture();
_spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight());
return _spectatorCameraNetworkTexture; return _spectatorCameraNetworkTexture;
} }
}
}
// FIXME: Generalize this, DRY up this code // FIXME: Generalize this, DRY up this code
if (resourceTextureUrl == HMD_PREVIEW_FRAME_URL) { if (resourceTextureUrl == HMD_PREVIEW_FRAME_URL) {
if (!_hmdPreviewNetworkTexture) { if (!_hmdPreviewNetworkTexture) {
@ -1052,7 +1049,18 @@ const gpu::FramebufferPointer& TextureCache::getSpectatorCameraFramebuffer(int w
// If we aren't taking a screenshot, we might need to resize or create the camera buffer // If we aren't taking a screenshot, we might need to resize or create the camera buffer
if (!_spectatorCameraFramebuffer || _spectatorCameraFramebuffer->getWidth() != width || _spectatorCameraFramebuffer->getHeight() != height) { if (!_spectatorCameraFramebuffer || _spectatorCameraFramebuffer->getWidth() != width || _spectatorCameraFramebuffer->getHeight() != height) {
_spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height)); _spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height));
updateSpectatorCameraNetworkTexture();
emit spectatorCameraFramebufferReset(); emit spectatorCameraFramebufferReset();
} }
return _spectatorCameraFramebuffer; return _spectatorCameraFramebuffer;
} }
void TextureCache::updateSpectatorCameraNetworkTexture() {
if (_spectatorCameraFramebuffer && _spectatorCameraNetworkTexture) {
gpu::TexturePointer texture = _spectatorCameraFramebuffer->getRenderBuffer(0);
if (texture) {
texture->setSource(SPECTATOR_CAMERA_FRAME_URL.toString().toStdString());
_spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight());
}
}
}

View file

@ -171,6 +171,7 @@ public:
const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height); const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height);
const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer();
const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height);
void updateSpectatorCameraNetworkTexture();
static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 }; static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 };
static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 }; static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 };

View file

@ -88,3 +88,56 @@ void UserActivityLoggerScriptingInterface::doLogAction(QString action, QJsonObje
Q_ARG(QString, action), Q_ARG(QString, action),
Q_ARG(QJsonObject, details)); Q_ARG(QJsonObject, details));
} }
void UserActivityLoggerScriptingInterface::commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem) {
QJsonObject payload;
payload["marketplaceID"] = marketplaceID;
payload["cost"] = cost;
payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem;
doLogAction("commercePurchaseSuccess", payload);
}
void UserActivityLoggerScriptingInterface::commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails) {
QJsonObject payload;
payload["marketplaceID"] = marketplaceID;
payload["cost"] = cost;
payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem;
payload["errorDetails"] = errorDetails;
doLogAction("commercePurchaseFailure", payload);
}
void UserActivityLoggerScriptingInterface::commerceEntityRezzed(QString marketplaceID, QString source, QString type) {
QJsonObject payload;
payload["marketplaceID"] = marketplaceID;
payload["source"] = source;
payload["type"] = type;
doLogAction("commerceEntityRezzed", payload);
}
void UserActivityLoggerScriptingInterface::commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain) {
QJsonObject payload;
payload["timestamp"] = timestamp;
payload["setupAttemptID"] = setupAttemptID;
payload["setupFlowVersion"] = setupFlowVersion;
payload["referrer"] = referrer;
payload["currentDomain"] = currentDomain;
doLogAction("commerceWalletSetupStarted", payload);
}
void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName) {
QJsonObject payload;
payload["timestamp"] = timestamp;
payload["setupAttemptID"] = setupAttemptID;
payload["secondsElapsed"] = secondsElapsed;
payload["currentStepNumber"] = currentStepNumber;
payload["currentStepName"] = currentStepName;
doLogAction("commerceWalletSetupProgress", payload);
}
void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(int timestamp, QString setupAttemptID, int secondsToComplete) {
QJsonObject payload;
payload["timestamp"] = timestamp;
payload["setupAttemptID"] = setupAttemptID;
payload["secondsToComplete"] = secondsToComplete;
doLogAction("commerceWalletSetupFinished", payload);
}

View file

@ -33,6 +33,12 @@ public:
Q_INVOKABLE void bubbleToggled(bool newValue); Q_INVOKABLE void bubbleToggled(bool newValue);
Q_INVOKABLE void bubbleActivated(); Q_INVOKABLE void bubbleActivated();
Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{}); Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{});
Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem);
Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails);
Q_INVOKABLE void commerceEntityRezzed(QString marketplaceID, QString source, QString type);
Q_INVOKABLE void commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain);
Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName);
Q_INVOKABLE void commerceWalletSetupFinished(int timestamp, QString setupAttemptID, int secondsToComplete);
private: private:
void doLogAction(QString action, QJsonObject details = {}); void doLogAction(QString action, QJsonObject details = {});
}; };

View file

@ -32,23 +32,23 @@ void Transaction::removeItem(ItemID id) {
} }
void Transaction::addTransitionToItem(ItemID id, Transition::Type transition, ItemID boundId) { void Transaction::addTransitionToItem(ItemID id, Transition::Type transition, ItemID boundId) {
_addedTransitions.emplace_back(TransitionAdd{ id, transition, boundId }); _addedTransitions.emplace_back(id, transition, boundId);
} }
void Transaction::removeTransitionFromItem(ItemID id) { void Transaction::removeTransitionFromItem(ItemID id) {
_addedTransitions.emplace_back(TransitionAdd{ id, Transition::NONE, render::Item::INVALID_ITEM_ID }); _addedTransitions.emplace_back(id, Transition::NONE, render::Item::INVALID_ITEM_ID);
} }
void Transaction::reApplyTransitionToItem(ItemID id) { void Transaction::reApplyTransitionToItem(ItemID id) {
_reAppliedTransitions.emplace_back(TransitionReApply{ id }); _reAppliedTransitions.emplace_back(id);
} }
void Transaction::queryTransitionOnItem(ItemID id, TransitionQueryFunc func) { void Transaction::queryTransitionOnItem(ItemID id, TransitionQueryFunc func) {
_queriedTransitions.emplace_back(TransitionQuery{ id, func }); _queriedTransitions.emplace_back(id, func);
} }
void Transaction::updateItem(ItemID id, const UpdateFunctorPointer& functor) { void Transaction::updateItem(ItemID id, const UpdateFunctorPointer& functor) {
_updatedItems.emplace_back(Update{ id, functor }); _updatedItems.emplace_back(id, functor);
} }
void Transaction::resetSelection(const Selection& selection) { void Transaction::resetSelection(const Selection& selection) {
@ -56,28 +56,122 @@ void Transaction::resetSelection(const Selection& selection) {
} }
void Transaction::resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style) { void Transaction::resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style) {
_highlightResets.emplace_back(HighlightReset{ selectionName, style }); _highlightResets.emplace_back(selectionName, style );
} }
void Transaction::removeHighlightFromSelection(const std::string& selectionName) { void Transaction::removeHighlightFromSelection(const std::string& selectionName) {
_highlightRemoves.emplace_back(selectionName); _highlightRemoves.emplace_back(selectionName);
} }
void Transaction::querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func) { void Transaction::querySelectionHighlight(const std::string& selectionName, const SelectionHighlightQueryFunc& func) {
_highlightQueries.emplace_back(HighlightQuery{ selectionName, func }); _highlightQueries.emplace_back(selectionName, func);
}
void Transaction::reserve(const std::vector<Transaction>& transactionContainer) {
size_t resetItemsCount = 0;
size_t removedItemsCount = 0;
size_t updatedItemsCount = 0;
size_t resetSelectionsCount = 0;
size_t addedTransitionsCount = 0;
size_t queriedTransitionsCount = 0;
size_t reAppliedTransitionsCount = 0;
size_t highlightResetsCount = 0;
size_t highlightRemovesCount = 0;
size_t highlightQueriesCount = 0;
for (const auto& transaction : transactionContainer) {
resetItemsCount += transaction._resetItems.size();
removedItemsCount += transaction._removedItems.size();
updatedItemsCount += transaction._updatedItems.size();
resetSelectionsCount += transaction._resetSelections.size();
addedTransitionsCount += transaction._addedTransitions.size();
queriedTransitionsCount += transaction._queriedTransitions.size();
reAppliedTransitionsCount += transaction._reAppliedTransitions.size();
highlightResetsCount += transaction._highlightResets.size();
highlightRemovesCount += transaction._highlightRemoves.size();
highlightQueriesCount += transaction._highlightQueries.size();
}
_resetItems.reserve(resetItemsCount);
_removedItems.reserve(removedItemsCount);
_updatedItems.reserve(updatedItemsCount);
_resetSelections.reserve(resetSelectionsCount);
_addedTransitions.reserve(addedTransitionsCount);
_queriedTransitions.reserve(queriedTransitionsCount);
_reAppliedTransitions.reserve(reAppliedTransitionsCount);
_highlightResets.reserve(highlightResetsCount);
_highlightRemoves.reserve(highlightRemovesCount);
_highlightQueries.reserve(highlightQueriesCount);
}
void Transaction::merge(const std::vector<Transaction>& transactionContainer) {
reserve(transactionContainer);
for (const auto& transaction : transactionContainer) {
merge(transaction);
}
}
void Transaction::merge(std::vector<Transaction>&& transactionContainer) {
reserve(transactionContainer);
auto begin = std::make_move_iterator(transactionContainer.begin());
auto end = std::make_move_iterator(transactionContainer.end());
for (auto itr = begin; itr != end; ++itr) {
merge(*itr);
}
transactionContainer.clear();
}
template <typename T>
void moveElements(T& target, T& source) {
target.insert(target.end(), std::make_move_iterator(source.begin()), std::make_move_iterator(source.end()));
source.clear();
}
template <typename T>
void copyElements(T& target, const T& source) {
target.insert(target.end(), source.begin(), source.end());
}
void Transaction::merge(Transaction&& transaction) {
moveElements(_resetItems, transaction._resetItems);
moveElements(_removedItems, transaction._removedItems);
moveElements(_updatedItems, transaction._updatedItems);
moveElements(_resetSelections, transaction._resetSelections);
moveElements(_addedTransitions, transaction._addedTransitions);
moveElements(_queriedTransitions, transaction._queriedTransitions);
moveElements(_reAppliedTransitions, transaction._reAppliedTransitions);
moveElements(_highlightResets, transaction._highlightResets);
moveElements(_highlightRemoves, transaction._highlightRemoves);
moveElements(_highlightQueries, transaction._highlightQueries);
} }
void Transaction::merge(const Transaction& transaction) { void Transaction::merge(const Transaction& transaction) {
_resetItems.insert(_resetItems.end(), transaction._resetItems.begin(), transaction._resetItems.end()); copyElements(_resetItems, transaction._resetItems);
_removedItems.insert(_removedItems.end(), transaction._removedItems.begin(), transaction._removedItems.end()); copyElements(_removedItems, transaction._removedItems);
_updatedItems.insert(_updatedItems.end(), transaction._updatedItems.begin(), transaction._updatedItems.end()); copyElements(_updatedItems, transaction._updatedItems);
_resetSelections.insert(_resetSelections.end(), transaction._resetSelections.begin(), transaction._resetSelections.end()); copyElements(_resetSelections, transaction._resetSelections);
_addedTransitions.insert(_addedTransitions.end(), transaction._addedTransitions.begin(), transaction._addedTransitions.end()); copyElements(_addedTransitions, transaction._addedTransitions);
_queriedTransitions.insert(_queriedTransitions.end(), transaction._queriedTransitions.begin(), transaction._queriedTransitions.end()); copyElements(_queriedTransitions, transaction._queriedTransitions);
_reAppliedTransitions.insert(_reAppliedTransitions.end(), transaction._reAppliedTransitions.begin(), transaction._reAppliedTransitions.end()); copyElements(_reAppliedTransitions, transaction._reAppliedTransitions);
_highlightResets.insert(_highlightResets.end(), transaction._highlightResets.begin(), transaction._highlightResets.end()); copyElements(_highlightResets, transaction._highlightResets);
_highlightRemoves.insert(_highlightRemoves.end(), transaction._highlightRemoves.begin(), transaction._highlightRemoves.end()); copyElements(_highlightRemoves, transaction._highlightRemoves);
_highlightQueries.insert(_highlightQueries.end(), transaction._highlightQueries.begin(), transaction._highlightQueries.end()); copyElements(_highlightQueries, transaction._highlightQueries);
}
void Transaction::clear() {
_resetItems.clear();
_removedItems.clear();
_updatedItems.clear();
_resetSelections.clear();
_addedTransitions.clear();
_queriedTransitions.clear();
_reAppliedTransitions.clear();
_highlightResets.clear();
_highlightRemoves.clear();
_highlightQueries.clear();
} }
@ -102,54 +196,50 @@ bool Scene::isAllocatedID(const ItemID& id) const {
/// Enqueue change batch to the scene /// Enqueue change batch to the scene
void Scene::enqueueTransaction(const Transaction& transaction) { void Scene::enqueueTransaction(const Transaction& transaction) {
_transactionQueueMutex.lock(); std::unique_lock<std::mutex> lock(_transactionQueueMutex);
_transactionQueue.push(transaction); _transactionQueue.emplace_back(transaction);
_transactionQueueMutex.unlock();
} }
void consolidateTransaction(TransactionQueue& queue, Transaction& singleBatch) { void Scene::enqueueTransaction(Transaction&& transaction) {
while (!queue.empty()) { std::unique_lock<std::mutex> lock(_transactionQueueMutex);
const auto& transaction = queue.front(); _transactionQueue.emplace_back(std::move(transaction));
singleBatch.merge(transaction);
queue.pop();
};
} }
uint32_t Scene::enqueueFrame() { uint32_t Scene::enqueueFrame() {
PROFILE_RANGE(render, __FUNCTION__); PROFILE_RANGE(render, __FUNCTION__);
Transaction consolidatedTransaction; TransactionQueue localTransactionQueue;
{ {
std::unique_lock<std::mutex> lock(_transactionQueueMutex); std::unique_lock<std::mutex> lock(_transactionQueueMutex);
consolidateTransaction(_transactionQueue, consolidatedTransaction); localTransactionQueue.swap(_transactionQueue);
} }
uint32_t frameNumber = 0; Transaction consolidatedTransaction;
consolidatedTransaction.merge(std::move(localTransactionQueue));
{ {
std::unique_lock<std::mutex> lock(_transactionFramesMutex); std::unique_lock<std::mutex> lock(_transactionFramesMutex);
_transactionFrames.push_back(consolidatedTransaction); _transactionFrames.push_back(consolidatedTransaction);
_transactionFrameNumber++;
frameNumber = _transactionFrameNumber;
} }
return frameNumber; return ++_transactionFrameNumber;
} }
void Scene::processTransactionQueue() { void Scene::processTransactionQueue() {
PROFILE_RANGE(render, __FUNCTION__); PROFILE_RANGE(render, __FUNCTION__);
TransactionFrames queuedFrames; static TransactionFrames queuedFrames;
{ {
// capture the queued frames and clear the queue // capture the queued frames and clear the queue
std::unique_lock<std::mutex> lock(_transactionFramesMutex); std::unique_lock<std::mutex> lock(_transactionFramesMutex);
queuedFrames = _transactionFrames; queuedFrames.swap(_transactionFrames);
_transactionFrames.clear();
} }
// go through the queue of frames and process them // go through the queue of frames and process them
for (auto& frame : queuedFrames) { for (auto& frame : queuedFrames) {
processTransactionFrame(frame); processTransactionFrame(frame);
} }
queuedFrames.clear();
} }
void Scene::processTransactionFrame(const Transaction& transaction) { void Scene::processTransactionFrame(const Transaction& transaction) {

View file

@ -65,9 +65,14 @@ public:
void resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle()); void resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle());
void removeHighlightFromSelection(const std::string& selectionName); void removeHighlightFromSelection(const std::string& selectionName);
void querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func); void querySelectionHighlight(const std::string& selectionName, const SelectionHighlightQueryFunc& func);
void reserve(const std::vector<Transaction>& transactionContainer);
void merge(const std::vector<Transaction>& transactionContainer);
void merge(std::vector<Transaction>&& transactionContainer);
void merge(const Transaction& transaction); void merge(const Transaction& transaction);
void merge(Transaction&& transaction);
void clear();
// Checkers if there is work to do when processing the transaction // Checkers if there is work to do when processing the transaction
bool touchTransactions() const { return !_resetSelections.empty(); } bool touchTransactions() const { return !_resetSelections.empty(); }
@ -107,7 +112,7 @@ protected:
HighlightRemoves _highlightRemoves; HighlightRemoves _highlightRemoves;
HighlightQueries _highlightQueries; HighlightQueries _highlightQueries;
}; };
typedef std::queue<Transaction> TransactionQueue; typedef std::vector<Transaction> TransactionQueue;
// Scene is a container for Items // Scene is a container for Items
@ -133,6 +138,9 @@ public:
// Enqueue transaction to the scene // Enqueue transaction to the scene
void enqueueTransaction(const Transaction& transaction); void enqueueTransaction(const Transaction& transaction);
// Enqueue transaction to the scene
void enqueueTransaction(Transaction&& transaction);
// Enqueue end of frame transactions boundary // Enqueue end of frame transactions boundary
uint32_t enqueueFrame(); uint32_t enqueueFrame();
@ -187,7 +195,7 @@ protected:
std::mutex _transactionFramesMutex; std::mutex _transactionFramesMutex;
using TransactionFrames = std::list<Transaction>; using TransactionFrames = std::vector<Transaction>;
TransactionFrames _transactionFrames; TransactionFrames _transactionFrames;
uint32_t _transactionFrameNumber{ 0 }; uint32_t _transactionFrameNumber{ 0 };

View file

@ -12,6 +12,9 @@
#define hifi_PrioritySortUtil_h #define hifi_PrioritySortUtil_h
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <queue>
#include "NumericalConstants.h"
#include "ViewFrustum.h" #include "ViewFrustum.h"
/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: /* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use:
@ -32,11 +35,10 @@
(2) Make a PrioritySortUtil::PriorityQueue<Thing> and add them to the queue: (2) Make a PrioritySortUtil::PriorityQueue<Thing> and add them to the queue:
PrioritySortUtil::Prioritizer prioritizer(viewFrustum); PrioritySortUtil::PriorityQueue<SortableWrapper> sortedThings(viewFrustum);
std::priority_queue< PrioritySortUtil::Sortable<Thing> > sortedThings; std::priority_queue< PrioritySortUtil::Sortable<Thing> > sortedThings;
for (thing in things) { for (thing in things) {
float priority = prioritizer.computePriority(PrioritySortUtil::PrioritizableThing(thing)); sortedThings.push(SortableWrapper(thing));
sortedThings.push(PrioritySortUtil::Sortable<Thing> entry(thing, priority));
} }
(3) Loop over your priority queue and do timeboxed work: (3) Loop over your priority queue and do timeboxed work:
@ -65,6 +67,7 @@ namespace PrioritySortUtil {
virtual uint64_t getTimestamp() const = 0; virtual uint64_t getTimestamp() const = 0;
void setPriority(float priority) { _priority = priority; } void setPriority(float priority) { _priority = priority; }
float getPriority() const { return _priority; }
bool operator<(const Sortable& other) const { return _priority < other._priority; } bool operator<(const Sortable& other) const { return _priority < other._priority; }
private: private:
float _priority { 0.0f }; float _priority { 0.0f };
@ -109,11 +112,19 @@ namespace PrioritySortUtil {
glm::vec3 position = thing.getPosition(); glm::vec3 position = thing.getPosition();
glm::vec3 offset = position - _view.getPosition(); glm::vec3 offset = position - _view.getPosition();
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
float radius = thing.getRadius(); const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance)
float radius = glm::min(thing.getRadius(), MIN_RADIUS);
float cosineAngle = (glm::dot(offset, _view.getDirection()) / distance);
float age = (float)(usecTimestampNow() - thing.getTimestamp());
float priority = _angularWeight * (radius / distance) // we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward
+ _centerWeight * (glm::dot(offset, _view.getDirection()) / distance) // at a reduced rate but we don't want the "age" term to go zero or negative so we clamp it
+ _ageWeight * (float)(usecTimestampNow() - thing.getTimestamp()); const float MIN_COSINE_ANGLE_FACTOR = 0.1f;
float cosineAngleFactor = glm::max(cosineAngle, MIN_COSINE_ANGLE_FACTOR);
float priority = _angularWeight * glm::max(radius, MIN_RADIUS) / distance
+ _centerWeight * cosineAngle
+ _ageWeight * cosineAngleFactor * age;
// decrement priority of things outside keyhole // decrement priority of things outside keyhole
if (distance - radius > _view.getCenterRadius()) { if (distance - radius > _view.getCenterRadius()) {

View file

@ -121,26 +121,20 @@ uint64_t uvec2ToUint64(const uvec2& v) {
class AudioHandler : public QObject, QRunnable { class AudioHandler : public QObject, QRunnable {
Q_OBJECT Q_OBJECT
public: public:
AudioHandler(QObject* container, const QString& deviceName, int runDelayMs = 0, QObject* parent = nullptr) : QObject(parent) { AudioHandler(QSharedPointer<OffscreenQmlSurface> surface, const QString& deviceName, QObject* parent = nullptr) : QObject(parent) {
_container = container;
_newTargetDevice = deviceName; _newTargetDevice = deviceName;
_runDelayMs = runDelayMs; _surface = surface;
setAutoDelete(true); setAutoDelete(true);
if (deviceName.size() > 0) {
QThreadPool::globalInstance()->start(this); QThreadPool::globalInstance()->start(this);
} }
}
virtual ~AudioHandler() { virtual ~AudioHandler() {
qDebug() << "Audio Handler Destroyed"; qDebug() << "Audio Handler Destroyed";
} }
void run() override { void run() override {
if (_newTargetDevice.isEmpty()) { if (!_surface.isNull() && _surface->getRootItem() && !_surface->getCleaned()) {
return; for (auto player : _surface->getRootItem()->findChildren<QMediaPlayer*>()) {
}
if (_runDelayMs > 0) {
QThread::msleep(_runDelayMs);
}
auto audioIO = DependencyManager::get<AudioClient>();
QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName();
for (auto player : _container->findChildren<QMediaPlayer*>()) {
auto mediaState = player->state(); auto mediaState = player->state();
QMediaService *svc = player->service(); QMediaService *svc = player->service();
if (nullptr == svc) { if (nullptr == svc) {
@ -156,7 +150,7 @@ public:
for (int i = 0; i < outputs.size(); i++) { for (int i = 0; i < outputs.size(); i++) {
QString output = outputs[i]; QString output = outputs[i];
QString description = out->outputDescription(output); QString description = out->outputDescription(output);
if (description == deviceName) { if (description == _newTargetDevice) {
deviceOuput = output; deviceOuput = output;
break; break;
} }
@ -167,17 +161,18 @@ public:
// this will reset it back to a paused state // this will reset it back to a paused state
if (mediaState == QMediaPlayer::State::PausedState) { if (mediaState == QMediaPlayer::State::PausedState) {
player->pause(); player->pause();
} else if (mediaState == QMediaPlayer::State::StoppedState) { }
else if (mediaState == QMediaPlayer::State::StoppedState) {
player->stop(); player->stop();
} }
} }
qDebug() << "QML Audio changed to " << deviceName; }
qDebug() << "QML Audio changed to " << _newTargetDevice;
} }
private: private:
QString _newTargetDevice; QString _newTargetDevice;
QObject* _container; QSharedPointer<OffscreenQmlSurface> _surface;
int _runDelayMs;
}; };
class OffscreenTextures { class OffscreenTextures {
@ -502,6 +497,7 @@ QOpenGLContext* OffscreenQmlSurface::getSharedContext() {
} }
void OffscreenQmlSurface::cleanup() { void OffscreenQmlSurface::cleanup() {
_isCleaned = true;
_canvas->makeCurrent(); _canvas->makeCurrent();
_renderControl->invalidate(); _renderControl->invalidate();
@ -600,6 +596,7 @@ OffscreenQmlSurface::OffscreenQmlSurface() {
OffscreenQmlSurface::~OffscreenQmlSurface() { OffscreenQmlSurface::~OffscreenQmlSurface() {
QObject::disconnect(&_updateTimer); QObject::disconnect(&_updateTimer);
disconnectAudioOutputTimer();
QObject::disconnect(qApp); QObject::disconnect(qApp);
cleanup(); cleanup();
@ -613,6 +610,15 @@ OffscreenQmlSurface::~OffscreenQmlSurface() {
void OffscreenQmlSurface::onAboutToQuit() { void OffscreenQmlSurface::onAboutToQuit() {
_paused = true; _paused = true;
QObject::disconnect(&_updateTimer); QObject::disconnect(&_updateTimer);
disconnectAudioOutputTimer();
}
void OffscreenQmlSurface::disconnectAudioOutputTimer() {
if (_audioOutputUpdateTimer.isActive()) {
_audioOutputUpdateTimer.stop();
}
QObject::disconnect(&_audioOutputUpdateTimer);
} }
void OffscreenQmlSurface::create() { void OffscreenQmlSurface::create() {
@ -671,6 +677,14 @@ void OffscreenQmlSurface::create() {
} }
}); });
// Setup the update of the QML media components with the current audio output device
QObject::connect(&_audioOutputUpdateTimer, &QTimer::timeout, this, [this]() {
new AudioHandler(sharedFromThis(), _currentAudioOutputDevice);
});
int waitForAudioQmlMs = 200;
_audioOutputUpdateTimer.setInterval(waitForAudioQmlMs);
_audioOutputUpdateTimer.setSingleShot(true);
// When Quick says there is a need to render, we will not render immediately. Instead, // When Quick says there is a need to render, we will not render immediately. Instead,
// a timer with a small interval is used to get better performance. // a timer with a small interval is used to get better performance.
QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick);
@ -699,10 +713,11 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() {
QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection);
} else { } else {
auto audioIO = DependencyManager::get<AudioClient>(); auto audioIO = DependencyManager::get<AudioClient>();
QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); _currentAudioOutputDevice = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName();
int waitForAudioQmlMs = 500; if (_audioOutputUpdateTimer.isActive()) {
// The audio device need to be change using oth _audioOutputUpdateTimer.stop();
new AudioHandler(_rootItem, deviceName, waitForAudioQmlMs); }
_audioOutputUpdateTimer.start();
} }
} }

View file

@ -40,7 +40,7 @@ class QQuickItem;
using QmlContextCallback = std::function<void(QQmlContext*, QObject*)>; using QmlContextCallback = std::function<void(QQmlContext*, QObject*)>;
class OffscreenQmlSurface : public QObject { class OffscreenQmlSurface : public QObject, public QEnableSharedFromThis<OffscreenQmlSurface> {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged) Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
public: public:
@ -75,6 +75,7 @@ public:
void pause(); void pause();
void resume(); void resume();
bool isPaused() const; bool isPaused() const;
bool getCleaned() { return _isCleaned; }
void setBaseUrl(const QUrl& baseUrl); void setBaseUrl(const QUrl& baseUrl);
QQuickItem* getRootItem(); QQuickItem* getRootItem();
@ -116,6 +117,7 @@ public slots:
void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false); void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false);
void forceHtmlAudioOutputDeviceUpdate(); void forceHtmlAudioOutputDeviceUpdate();
void forceQmlAudioOutputDeviceUpdate(); void forceQmlAudioOutputDeviceUpdate();
signals: signals:
void audioOutputDeviceChanged(const QString& deviceName); void audioOutputDeviceChanged(const QString& deviceName);
@ -147,6 +149,7 @@ private:
void render(); void render();
void cleanup(); void cleanup();
QJsonObject getGLContextData(); QJsonObject getGLContextData();
void disconnectAudioOutputTimer();
private slots: private slots:
void updateQuick(); void updateQuick();
@ -170,6 +173,9 @@ private:
uint64_t _lastRenderTime { 0 }; uint64_t _lastRenderTime { 0 };
uvec2 _size; uvec2 _size;
QTimer _audioOutputUpdateTimer;
QString _currentAudioOutputDevice;
// Texture management // Texture management
TextureAndFence _latestTextureAndFence { 0, 0 }; TextureAndFence _latestTextureAndFence { 0, 0 };
@ -177,6 +183,7 @@ private:
bool _polish { true }; bool _polish { true };
bool _paused { true }; bool _paused { true };
bool _focusText { false }; bool _focusText { false };
bool _isCleaned{ false };
uint8_t _maxFps { 60 }; uint8_t _maxFps { 60 };
MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } }; MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } };
QWindow* _proxyWindow { nullptr }; QWindow* _proxyWindow { nullptr };

View file

@ -62,7 +62,7 @@
this.pointingAtTablet = function(controllerData) { this.pointingAtTablet = function(controllerData) {
var rayPick = controllerData.rayPicks[this.hand]; var rayPick = controllerData.rayPicks[this.hand];
return (rayPick.objectID === HMD.tabletScreenID || rayPick.objectID === HMD.homeButtonID); return (HMD.tabletScreenID && HMD.homeButtonID && (rayPick.objectID === HMD.tabletScreenID || rayPick.objectID === HMD.homeButtonID));
}; };
this.getOtherModule = function() { this.getOtherModule = function() {

View file

@ -81,7 +81,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
}; };
this.otherHandIsParent = function(props) { this.otherHandIsParent = function(props) {
return this.getOtherModule().thisHandIsParent(props); var otherModule = this.getOtherModule();
return (otherModule.thisHandIsParent(props) && otherModule.grabbing);
}; };
this.startNearParentingGrabEntity = function (controllerData, targetProps) { this.startNearParentingGrabEntity = function (controllerData, targetProps) {

View file

@ -63,7 +63,7 @@ var toolBar = (function() {
y: -TOOLBAR_MARGIN_Y - toolHeight y: -TOOLBAR_MARGIN_Y - toolHeight
}); });
browseDirectoryButton = toolBar.addTool({ browseDirectoryButton = toolBar.addTool({
imageURL: toolIconUrl + "directory-01.svg", imageURL: toolIconUrl + "directory.svg",
subImage: { subImage: {
x: 0, x: 0,
y: Tool.IMAGE_WIDTH, y: Tool.IMAGE_WIDTH,

View file

@ -1415,6 +1415,9 @@ input#reset-to-natural-dimensions {
} }
/* ----- Order of Menu items for Primitive ----- */ /* ----- Order of Menu items for Primitive ----- */
/* Entity Menu classes are specified by selected entity
within entityProperties.js
*/
#properties-list.ShapeMenu #general, #properties-list.ShapeMenu #general,
#properties-list.BoxMenu #general, #properties-list.BoxMenu #general,
#properties-list.SphereMenu #general { #properties-list.SphereMenu #general {
@ -1469,6 +1472,34 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* ----- ParticleEffectMenu ----- */
#properties-list.ParticleEffectMenu #general {
order: 1;
}
#properties-list.ParticleEffectMenu #collision-info {
order: 2;
}
#properties-list.ParticleEffectMenu #physical {
order: 3;
}
#properties-list.ParticleEffectMenu #spatial {
order: 4;
}
#properties-list.ParticleEffectMenu #behavior {
order: 5;
}
/* items to hide */
#properties-list.ParticleEffectMenu #base-color-section,
#properties-list.ParticleEffectMenu #hyperlink,
#properties-list.ParticleEffectMenu #light,
#properties-list.ParticleEffectMenu #model,
#properties-list.ParticleEffectMenu #shape-list,
#properties-list.ParticleEffectMenu #text,
#properties-list.ParticleEffectMenu #web,
#properties-list.ParticleEffectMenu #zone {
display: none;
}
/* ----- Order of Menu items for Light ----- */ /* ----- Order of Menu items for Light ----- */
#properties-list.LightMenu #general { #properties-list.LightMenu #general {
@ -1500,8 +1531,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.LightMenu .shape-group.shape-section.property.dropdown, #properties-list.LightMenu #shape-list,
#properties-list.LightMenu color-section.color-control1 { #properties-list.LightMenu #base-color-section {
display: none display: none
} }
@ -1536,8 +1567,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.ModelMenu .shape-group.shape-section.property.dropdown, #properties-list.ModelMenu #shape-list,
#properties-list.ModelMenu .color-section.color-control1 { #properties-list.ModelMenu #base-color-section {
display: none display: none
} }
@ -1572,8 +1603,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.ZoneMenu .shape-group.shape-section.property.dropdown, #properties-list.ZoneMenu #shape-list,
#properties-list.ZoneMenu .color-section.color-control1 { #properties-list.ZoneMenu #base-color-section {
display: none display: none
} }
@ -1608,8 +1639,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.WebMenu .shape-group.shape-section.property.dropdown, #properties-list.WebMenu #shape-list,
#properties-list.WebMenu .color-section.color-control1 { #properties-list.WebMenu #base-color-section {
display: none; display: none;
} }
@ -1645,8 +1676,8 @@ input#reset-to-natural-dimensions {
display: none; display: none;
} }
/* items to hide */ /* items to hide */
#properties-list.TextMenu .shape-group.shape-section.property.dropdown, #properties-list.TextMenu #shape-list,
#properties-list.TextMenu .color-section.color-control1 { #properties-list.TextMenu #base-color-section {
display: none display: none
} }

View file

@ -44,7 +44,7 @@
<fieldset id="general" class="major"> <fieldset id="general" class="major">
<div class="shape-group shape-section property dropdown"> <div class="shape-group shape-section property dropdown" id="shape-list">
<label for="property-shape">Shape</label> <label for="property-shape">Shape</label>
<select name="SelectShape" id="property-shape"> <select name="SelectShape" id="property-shape">
<option value="Cube">Box</option> <option value="Cube">Box</option>
@ -66,12 +66,19 @@
<label for="property-name">Name</label> <label for="property-name">Name</label>
<input type="text" id="property-name"> <input type="text" id="property-name">
</div> </div>
<div class="physical-group color-section property color-control1"> <div class="physical-group color-section property rgb fstuple" id="base-color-section">
<div class="color-picker" id="property-color-control1"></div> <div class="color-picker" id="property-color-control2"></div>
<label>Entity color</label> <label>Entity color</label>
<div class="tuple">
<div><input type="number" class="red" id="property-color-red"><label for="property-color-red">Red:</label></div>
<div><input type="number" class="green" id="property-color-green"><label for="property-color-green">Green:</label></div>
<div><input type="number" class="blue" id="property-color-blue"><label for="property-color-blue">Blue:</label></div>
</div>
</div> </div>
</fieldset> </fieldset>
<fieldset id="collision-info">
<fieldset id="collision-info" class="major">
<legend class="section-header"> Collision<span>M</span> </legend>
<fieldset class="minor"> <fieldset class="minor">
<div class="behavior-group property checkbox"> <div class="behavior-group property checkbox">
<input type="checkbox" id="property-collisionless"> <input type="checkbox" id="property-collisionless">
@ -216,17 +223,6 @@
</div> </div>
</fieldset> </fieldset>
</fieldset> </fieldset>
<fieldset class="minor">
<div class="physical-group color-section property rgb fstuple">
<div class="color-picker" id="property-color-control2"></div>
<label>Entity color</label>
<div class="tuple">
<div><input type="number" class="red" id="property-color-red"><label for="property-color-red">Red:</label></div>
<div><input type="number" class="green" id="property-color-green"><label for="property-color-green">Green:</label></div>
<div><input type="number" class="blue" id="property-color-blue"><label for="property-color-blue">Blue:</label></div>
</div>
</div>
</fieldset>
</fieldset> </fieldset>

View file

@ -6,6 +6,9 @@
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global alert, augmentSpinButtons, clearTimeout, console, document, Element, EventBridge,
HifiEntityUI, JSONEditor, openEventBridge, setUpKeyboardControl, setTimeout, window, _ $ */
var PI = 3.14159265358979; var PI = 3.14159265358979;
var DEGREES_TO_RADIANS = PI / 180.0; var DEGREES_TO_RADIANS = PI / 180.0;
var RADIANS_TO_DEGREES = 180.0 / PI; var RADIANS_TO_DEGREES = 180.0 / PI;
@ -22,33 +25,33 @@ var ICON_FOR_TYPE = {
PolyVox: "&#xe005;", PolyVox: "&#xe005;",
Multiple: "&#xe000;", Multiple: "&#xe000;",
PolyLine: "&#xe01b;" PolyLine: "&#xe01b;"
} };
var EDITOR_TIMEOUT_DURATION = 1500; var EDITOR_TIMEOUT_DURATION = 1500;
const KEY_P = 80; //Key code for letter p used for Parenting hotkey. var KEY_P = 80; // Key code for letter p used for Parenting hotkey.
var colorPickers = []; var colorPickers = [];
var lastEntityID = null; var lastEntityID = null;
debugPrint = function(message) { function debugPrint(message) {
EventBridge.emitWebEvent( EventBridge.emitWebEvent(
JSON.stringify({ JSON.stringify({
type: "print", type: "print",
message: message message: message
}) })
); );
}; }
function enableChildren(el, selector) { function enableChildren(el, selector) {
els = el.querySelectorAll(selector); var elSelectors = el.querySelectorAll(selector);
for (var i = 0; i < els.length; i++) { for (var selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) {
els[i].removeAttribute('disabled'); elSelectors[selectorIndex].removeAttribute('disabled');
} }
} }
function disableChildren(el, selector) { function disableChildren(el, selector) {
els = el.querySelectorAll(selector); var elSelectors = el.querySelectorAll(selector);
for (var i = 0; i < els.length; i++) { for (var selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) {
els[i].setAttribute('disabled', 'disabled'); elSelectors[selectorIndex].setAttribute('disabled', 'disabled');
} }
} }
@ -103,16 +106,6 @@ function createEmitCheckedPropertyUpdateFunction(propertyName) {
}; };
} }
function createEmitCheckedToStringPropertyUpdateFunction(checkboxElement, name, propertyName) {
var newString = "";
if (checkboxElement.checked) {
newString += name + "";
} else {
}
}
function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) { function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) {
return function() { return function() {
var properties = {}; var properties = {};
@ -123,7 +116,7 @@ function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) {
} }
function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { function createEmitNumberPropertyUpdateFunction(propertyName, decimals) {
decimals = decimals == undefined ? 4 : decimals; decimals = ((decimals === undefined) ? 4 : decimals);
return function() { return function() {
var value = parseFloat(this.value).toFixed(decimals); var value = parseFloat(this.value).toFixed(decimals);
updateProperty(propertyName, value); updateProperty(propertyName, value);
@ -146,7 +139,9 @@ function createEmitTextPropertyUpdateFunction(propertyName) {
}; };
} }
function createZoneComponentModeChangedFunction(zoneComponent, zoneComponentModeInherit, zoneComponentModeDisabled, zoneComponentModeEnabled) { function createZoneComponentModeChangedFunction(zoneComponent, zoneComponentModeInherit,
zoneComponentModeDisabled, zoneComponentModeEnabled) {
return function() { return function() {
var zoneComponentMode; var zoneComponentMode;
@ -159,7 +154,7 @@ function createZoneComponentModeChangedFunction(zoneComponent, zoneComponentMode
} }
updateProperty(zoneComponent, zoneComponentMode); updateProperty(zoneComponent, zoneComponentMode);
} };
} }
function createEmitGroupTextPropertyUpdateFunction(group, propertyName) { function createEmitGroupTextPropertyUpdateFunction(group, propertyName) {
@ -177,11 +172,11 @@ function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) {
properties[property] = { properties[property] = {
x: elX.value, x: elX.value,
y: elY.value, y: elY.value,
z: elZ.value, z: elZ.value
}; };
updateProperties(properties); updateProperties(properties);
}
}; };
}
function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) { function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) {
return function() { return function() {
@ -190,11 +185,11 @@ function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, el
properties[group][property] = { properties[group][property] = {
x: elX.value, x: elX.value,
y: elY.value, y: elY.value,
z: elZ ? elZ.value : 0, z: elZ ? elZ.value : 0
}; };
updateProperties(properties); updateProperties(properties);
}
}; };
}
function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) { function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) {
return function() { return function() {
@ -202,17 +197,17 @@ function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY,
properties[property] = { properties[property] = {
x: elX.value * multiplier, x: elX.value * multiplier,
y: elY.value * multiplier, y: elY.value * multiplier,
z: elZ.value * multiplier, z: elZ.value * multiplier
}; };
updateProperties(properties); updateProperties(properties);
}
}; };
}
function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) { function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) {
return function() { return function() {
emitColorPropertyUpdate(property, elRed.value, elGreen.value, elBlue.value); emitColorPropertyUpdate(property, elRed.value, elGreen.value, elBlue.value);
}
}; };
}
function emitColorPropertyUpdate(property, red, green, blue, group) { function emitColorPropertyUpdate(property, red, green, blue, group) {
var properties = {}; var properties = {};
@ -221,17 +216,17 @@ function emitColorPropertyUpdate(property, red, green, blue, group) {
properties[group][property] = { properties[group][property] = {
red: red, red: red,
green: green, green: green,
blue: blue, blue: blue
}; };
} else { } else {
properties[property] = { properties[property] = {
red: red, red: red,
green: green, green: green,
blue: blue, blue: blue
}; };
} }
updateProperties(properties); updateProperties(properties);
}; }
function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) { function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) {
@ -241,11 +236,11 @@ function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGr
properties[group][property] = { properties[group][property] = {
red: elRed.value, red: elRed.value,
green: elGreen.value, green: elGreen.value,
blue: elBlue.value, blue: elBlue.value
}; };
updateProperties(properties); updateProperties(properties);
}
}; };
}
function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) { function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) {
if (subPropertyElement.checked) { if (subPropertyElement.checked) {
@ -264,12 +259,12 @@ function setUserDataFromEditor(noUpdate) {
try { try {
json = editor.get(); json = editor.get();
} catch (e) { } catch (e) {
alert('Invalid JSON code - look for red X in your code ', +e) alert('Invalid JSON code - look for red X in your code ', +e);
} }
if (json === null) { if (json === null) {
return; return;
} else { } else {
var text = editor.getText() var text = editor.getText();
if (noUpdate === true) { if (noUpdate === true) {
EventBridge.emitWebEvent( EventBridge.emitWebEvent(
JSON.stringify({ JSON.stringify({
@ -277,7 +272,7 @@ function setUserDataFromEditor(noUpdate) {
type: "saveUserData", type: "saveUserData",
properties: { properties: {
userData: text userData: text
}, }
}) })
); );
return; return;
@ -297,10 +292,12 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults) {
} else { } else {
parsedData = JSON.parse(userDataElement.value); parsedData = JSON.parse(userDataElement.value);
} }
} catch (e) {} } catch (e) {
// TODO: Should an alert go here?
}
if (!(groupName in parsedData)) { if (!(groupName in parsedData)) {
parsedData[groupName] = {} parsedData[groupName] = {};
} }
var keys = Object.keys(updateKeyPair); var keys = Object.keys(updateKeyPair);
keys.forEach(function (key) { keys.forEach(function (key) {
@ -322,16 +319,16 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults) {
} }
} }
}); });
if (Object.keys(parsedData[groupName]).length == 0) { if (Object.keys(parsedData[groupName]).length === 0) {
delete parsedData[groupName]; delete parsedData[groupName];
} }
if (Object.keys(parsedData).length > 0) { if (Object.keys(parsedData).length > 0) {
properties['userData'] = JSON.stringify(parsedData); properties.userData = JSON.stringify(parsedData);
} else { } else {
properties['userData'] = ''; properties.userData = '';
} }
userDataElement.value = properties['userData']; userDataElement.value = properties.userData;
updateProperties(properties); updateProperties(properties);
} }
@ -340,13 +337,12 @@ function userDataChanger(groupName, keyName, values, userDataElement, defaultVal
val[keyName] = values; val[keyName] = values;
def[keyName] = defaultValue; def[keyName] = defaultValue;
multiDataUpdater(groupName, val, userDataElement, def); multiDataUpdater(groupName, val, userDataElement, def);
}; }
function setTextareaScrolling(element) { function setTextareaScrolling(element) {
var isScrolling = element.scrollHeight > element.offsetHeight; var isScrolling = element.scrollHeight > element.offsetHeight;
element.setAttribute("scrolling", isScrolling ? "true" : "false"); element.setAttribute("scrolling", isScrolling ? "true" : "false");
}; }
var editor = null; var editor = null;
@ -364,7 +360,7 @@ function createJSONEditor() {
$('.jsoneditor-poweredBy').remove(); $('.jsoneditor-poweredBy').remove();
}, },
onError: function(e) { onError: function(e) {
alert('JSON editor:' + e) alert('JSON editor:' + e);
}, },
onChange: function() { onChange: function() {
var currentJSONString = editor.getText(); var currentJSONString = editor.getText();
@ -372,22 +368,22 @@ function createJSONEditor() {
if (currentJSONString === '{"":""}') { if (currentJSONString === '{"":""}') {
return; return;
} }
$('#userdata-save').attr('disabled', false) $('#userdata-save').attr('disabled', false);
} }
}; };
editor = new JSONEditor(container, options); editor = new JSONEditor(container, options);
}; }
function hideNewJSONEditorButton() { function hideNewJSONEditorButton() {
$('#userdata-new-editor').hide(); $('#userdata-new-editor').hide();
}; }
function hideClearUserDataButton() { function hideClearUserDataButton() {
$('#userdata-clear').hide(); $('#userdata-clear').hide();
}; }
function showSaveUserDataButton() { function showSaveUserDataButton() {
$('#userdata-save').show(); $('#userdata-save').show();
@ -401,65 +397,65 @@ function hideSaveUserDataButton() {
function showNewJSONEditorButton() { function showNewJSONEditorButton() {
$('#userdata-new-editor').show(); $('#userdata-new-editor').show();
}; }
function showClearUserDataButton() { function showClearUserDataButton() {
$('#userdata-clear').show(); $('#userdata-clear').show();
}; }
function showUserDataTextArea() { function showUserDataTextArea() {
$('#property-user-data').show(); $('#property-user-data').show();
}; }
function hideUserDataTextArea() { function hideUserDataTextArea() {
$('#property-user-data').hide(); $('#property-user-data').hide();
}; }
function showStaticUserData() { function showStaticUserData() {
if (editor !== null) { if (editor !== null) {
$('#static-userdata').show(); $('#static-userdata').show();
$('#static-userdata').css('height', $('#userdata-editor').height()) $('#static-userdata').css('height', $('#userdata-editor').height());
$('#static-userdata').text(editor.getText()); $('#static-userdata').text(editor.getText());
} }
}; }
function removeStaticUserData() { function removeStaticUserData() {
$('#static-userdata').hide(); $('#static-userdata').hide();
}; }
function setEditorJSON(json) { function setEditorJSON(json) {
editor.set(json) editor.set(json);
if (editor.hasOwnProperty('expandAll')) { if (editor.hasOwnProperty('expandAll')) {
editor.expandAll(); editor.expandAll();
} }
}; }
function getEditorJSON() { function getEditorJSON() {
return editor.get(); return editor.get();
}; }
function deleteJSONEditor() { function deleteJSONEditor() {
if (editor !== null) { if (editor !== null) {
editor.destroy(); editor.destroy();
editor = null; editor = null;
} }
}; }
var savedJSONTimer = null; var savedJSONTimer = null;
function saveJSONUserData(noUpdate) { function saveJSONUserData(noUpdate) {
setUserDataFromEditor(noUpdate); setUserDataFromEditor(noUpdate);
$('#userdata-saved').show(); $('#userdata-saved').show();
$('#userdata-save').attr('disabled', true) $('#userdata-save').attr('disabled', true);
if (savedJSONTimer !== null) { if (savedJSONTimer !== null) {
clearTimeout(savedJSONTimer); clearTimeout(savedJSONTimer);
} }
savedJSONTimer = setTimeout(function() { savedJSONTimer = setTimeout(function() {
$('#userdata-saved').hide(); $('#userdata-saved').hide();
}, 1500) }, EDITOR_TIMEOUT_DURATION);
} }
function bindAllNonJSONEditorElements() { function bindAllNonJSONEditorElements() {
@ -468,6 +464,8 @@ function bindAllNonJSONEditorElements() {
for (i = 0; i < inputs.length; i++) { for (i = 0; i < inputs.length; i++) {
var input = inputs[i]; var input = inputs[i];
var field = $(input); var field = $(input);
// TODO FIXME: (JSHint) Functions declared within loops referencing
// an outer scoped variable may lead to confusing semantics.
field.on('focus', function(e) { field.on('focus', function(e) {
if (e.target.id === "userdata-new-editor" || e.target.id === "userdata-clear") { if (e.target.id === "userdata-new-editor" || e.target.id === "userdata-clear") {
return; return;
@ -477,7 +475,7 @@ function bindAllNonJSONEditorElements() {
} }
} }
}) });
} }
} }
@ -503,7 +501,6 @@ function clearSelection() {
function loaded() { function loaded() {
openEventBridge(function() { openEventBridge(function() {
var allSections = [];
var elPropertiesList = document.getElementById("properties-list"); var elPropertiesList = document.getElementById("properties-list");
var elID = document.getElementById("property-id"); var elID = document.getElementById("property-id");
var elType = document.getElementById("property-type"); var elType = document.getElementById("property-type");
@ -589,21 +586,14 @@ function loaded() {
var elUserData = document.getElementById("property-user-data"); var elUserData = document.getElementById("property-user-data");
var elClearUserData = document.getElementById("userdata-clear"); var elClearUserData = document.getElementById("userdata-clear");
var elSaveUserData = document.getElementById("userdata-save"); var elSaveUserData = document.getElementById("userdata-save");
var elJSONEditor = document.getElementById("userdata-editor");
var elNewJSONEditor = document.getElementById('userdata-new-editor'); var elNewJSONEditor = document.getElementById('userdata-new-editor');
var elColorSections = document.querySelectorAll(".color-section"); var elColorControlVariant2 = document.getElementById("property-color-control2");
var elColorControl1 = document.getElementById("property-color-control1");
var elColorControl2 = document.getElementById("property-color-control2");
var elColorRed = document.getElementById("property-color-red"); var elColorRed = document.getElementById("property-color-red");
var elColorGreen = document.getElementById("property-color-green"); var elColorGreen = document.getElementById("property-color-green");
var elColorBlue = document.getElementById("property-color-blue"); var elColorBlue = document.getElementById("property-color-blue");
var elShapeSections = document.querySelectorAll(".shape-section");
allSections.push(elShapeSections);
var elShape = document.getElementById("property-shape"); var elShape = document.getElementById("property-shape");
var elLightSections = document.querySelectorAll(".light-section");
allSections.push(elLightSections);
var elLightSpotLight = document.getElementById("property-light-spot-light"); var elLightSpotLight = document.getElementById("property-light-spot-light");
var elLightColor = document.getElementById("property-light-color"); var elLightColor = document.getElementById("property-light-color");
var elLightColorRed = document.getElementById("property-light-color-red"); var elLightColorRed = document.getElementById("property-light-color-red");
@ -615,8 +605,6 @@ function loaded() {
var elLightExponent = document.getElementById("property-light-exponent"); var elLightExponent = document.getElementById("property-light-exponent");
var elLightCutoff = document.getElementById("property-light-cutoff"); var elLightCutoff = document.getElementById("property-light-cutoff");
var elModelSections = document.querySelectorAll(".model-section");
allSections.push(elModelSections);
var elModelURL = document.getElementById("property-model-url"); var elModelURL = document.getElementById("property-model-url");
var elShapeType = document.getElementById("property-shape-type"); var elShapeType = document.getElementById("property-shape-type");
var elCompoundShapeURL = document.getElementById("property-compound-shape-url"); var elCompoundShapeURL = document.getElementById("property-compound-shape-url");
@ -632,8 +620,6 @@ function loaded() {
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");
var elWebSections = document.querySelectorAll(".web-section");
allSections.push(elWebSections);
var elWebSourceURL = document.getElementById("property-web-source-url"); var elWebSourceURL = document.getElementById("property-web-source-url");
var elWebDPI = document.getElementById("property-web-dpi"); var elWebDPI = document.getElementById("property-web-dpi");
@ -641,11 +627,7 @@ function loaded() {
var elHyperlinkHref = document.getElementById("property-hyperlink-href"); var elHyperlinkHref = document.getElementById("property-hyperlink-href");
var elHyperlinkSections = document.querySelectorAll(".hyperlink-section");
var elTextSections = document.querySelectorAll(".text-section");
allSections.push(elTextSections);
var elTextText = document.getElementById("property-text-text"); var elTextText = document.getElementById("property-text-text");
var elTextLineHeight = document.getElementById("property-text-line-height"); var elTextLineHeight = document.getElementById("property-text-line-height");
var elTextTextColor = document.getElementById("property-text-text-color"); var elTextTextColor = document.getElementById("property-text-text-color");
@ -653,13 +635,10 @@ function loaded() {
var elTextTextColorRed = document.getElementById("property-text-text-color-red"); var elTextTextColorRed = document.getElementById("property-text-text-color-red");
var elTextTextColorGreen = document.getElementById("property-text-text-color-green"); var elTextTextColorGreen = document.getElementById("property-text-text-color-green");
var elTextTextColorBlue = document.getElementById("property-text-text-color-blue"); var elTextTextColorBlue = document.getElementById("property-text-text-color-blue");
var elTextBackgroundColor = document.getElementById("property-text-background-color");
var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red"); var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red");
var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green"); var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green");
var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue"); var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue");
var elZoneSections = document.querySelectorAll(".zone-section");
allSections.push(elZoneSections);
var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled"); var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled");
var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color"); var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color");
@ -670,7 +649,6 @@ function loaded() {
var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity"); var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity");
var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x"); var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x");
var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y");
var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z");
var elZoneKeyLightAmbientURL = document.getElementById("property-zone-key-ambient-url"); var elZoneKeyLightAmbientURL = document.getElementById("property-zone-key-ambient-url");
var elZoneHazeModeInherit = document.getElementById("property-zone-haze-mode-inherit"); var elZoneHazeModeInherit = document.getElementById("property-zone-haze-mode-inherit");
@ -695,10 +673,6 @@ function loaded() {
var elZoneHazeBackgroundBlend = document.getElementById("property-zone-haze-background-blend"); var elZoneHazeBackgroundBlend = document.getElementById("property-zone-haze-background-blend");
var elZoneHazeAttenuateKeyLight = document.getElementById("property-zone-haze-attenuate-keylight");
var elZoneHazeKeyLightRange = document.getElementById("property-zone-haze-keylight-range");
var elZoneHazeKeyLightAltitude = document.getElementById("property-zone-haze-keylight-altitude");
var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude"); var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude");
var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude"); var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude");
var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude"); var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude");
@ -718,8 +692,6 @@ function loaded() {
var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed"); var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed");
var elZoneFilterURL = document.getElementById("property-zone-filter-url"); var elZoneFilterURL = document.getElementById("property-zone-filter-url");
var elPolyVoxSections = document.querySelectorAll(".poly-vox-section");
allSections.push(elPolyVoxSections);
var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x"); var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x");
var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y"); var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y");
var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z");
@ -732,7 +704,7 @@ function loaded() {
var properties; var properties;
EventBridge.scriptEventReceived.connect(function(data) { EventBridge.scriptEventReceived.connect(function(data) {
data = JSON.parse(data); data = JSON.parse(data);
if (data.type == "server_script_status") { if (data.type === "server_script_status") {
elServerScriptError.value = data.errorInfo; elServerScriptError.value = data.errorInfo;
// If we just set elServerScriptError's diplay to block or none, we still end up with // If we just set elServerScriptError's diplay to block or none, we still end up with
// it's parent contributing 21px bottom padding even when elServerScriptError is display:none. // it's parent contributing 21px bottom padding even when elServerScriptError is display:none.
@ -744,18 +716,18 @@ function loaded() {
var ENTITY_SCRIPT_STATUS = { var ENTITY_SCRIPT_STATUS = {
pending: "Pending", pending: "Pending",
loading: "Loading", loading: "Loading",
error_loading_script: "Error loading script", error_loading_script: "Error loading script", // eslint-disable-line camelcase
error_running_script: "Error running script", error_running_script: "Error running script", // eslint-disable-line camelcase
running: "Running", running: "Running",
unloaded: "Unloaded", unloaded: "Unloaded"
}; };
elServerScriptStatus.innerText = ENTITY_SCRIPT_STATUS[data.status] || data.status; elServerScriptStatus.innerText = ENTITY_SCRIPT_STATUS[data.status] || data.status;
} else { } else {
elServerScriptStatus.innerText = "Not running"; elServerScriptStatus.innerText = "Not running";
} }
} else if (data.type == "update") { } else if (data.type === "update") {
if (!data.selections || data.selections.length == 0) { if (!data.selections || data.selections.length === 0) {
if (editor !== null && lastEntityID !== null) { if (editor !== null && lastEntityID !== null) {
saveJSONUserData(true); saveJSONUserData(true);
deleteJSONEditor(); deleteJSONEditor();
@ -775,20 +747,19 @@ function loaded() {
for (var i = 0; i < selections.length; i++) { for (var i = 0; i < selections.length; i++) {
ids.push(selections[i].id); ids.push(selections[i].id);
var type = selections[i].properties.type; var currentSelectedType = selections[i].properties.type;
if (types[type] === undefined) { if (types[currentSelectedType] === undefined) {
types[type] = 0; types[currentSelectedType] = 0;
numTypes += 1; numTypes += 1;
} }
types[type]++; types[currentSelectedType]++;
} }
var type; var type = "Multiple";
if (numTypes === 1) { if (numTypes === 1) {
type = selections[0].properties.type; type = selections[0].properties.type;
} else {
type = "Multiple";
} }
elType.innerHTML = type + " (" + data.selections.length + ")"; elType.innerHTML = type + " (" + data.selections.length + ")";
elTypeIcon.innerHTML = ICON_FOR_TYPE[type]; elTypeIcon.innerHTML = ICON_FOR_TYPE[type];
elTypeIcon.style.display = "inline-block"; elTypeIcon.style.display = "inline-block";
@ -809,7 +780,9 @@ function loaded() {
lastEntityID = '"' + properties.id + '"'; lastEntityID = '"' + properties.id + '"';
elID.value = properties.id; elID.value = properties.id;
// Create class name for css ruleset filtering
elPropertiesList.className = properties.type + 'Menu'; elPropertiesList.className = properties.type + 'Menu';
elType.innerHTML = properties.type; elType.innerHTML = properties.type;
elTypeIcon.innerHTML = ICON_FOR_TYPE[properties.type]; elTypeIcon.innerHTML = ICON_FOR_TYPE[properties.type];
elTypeIcon.style.display = "inline-block"; elTypeIcon.style.display = "inline-block";
@ -883,13 +856,13 @@ function loaded() {
elCloneableLifetime.value = 300; elCloneableLifetime.value = 300;
var grabbablesSet = false; var grabbablesSet = false;
var parsedUserData = {} var parsedUserData = {};
try { try {
parsedUserData = JSON.parse(properties.userData); parsedUserData = JSON.parse(properties.userData);
if ("grabbableKey" in parsedUserData) { if ("grabbableKey" in parsedUserData) {
grabbablesSet = true; grabbablesSet = true;
var grabbableData = parsedUserData["grabbableKey"]; var grabbableData = parsedUserData.grabbableKey;
if ("grabbable" in grabbableData) { if ("grabbable" in grabbableData) {
elGrabbable.checked = grabbableData.grabbable; elGrabbable.checked = grabbableData.grabbable;
} else { } else {
@ -928,6 +901,7 @@ function loaded() {
} }
} }
} catch (e) { } catch (e) {
// TODO: What should go here?
} }
if (!grabbablesSet) { if (!grabbablesSet) {
elGrabbable.checked = true; elGrabbable.checked = true;
@ -968,19 +942,21 @@ function loaded() {
elDescription.value = properties.description; elDescription.value = properties.description;
if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere") { if (properties.type === "Shape" || properties.type === "Box" || properties.type === "Sphere") {
elShape.value = properties.shape; elShape.value = properties.shape;
setDropdownText(elShape); setDropdownText(elShape);
} }
if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") { if (properties.type === "Shape" || properties.type === "Box" ||
properties.type === "Sphere" || properties.type === "ParticleEffect") {
elColorRed.value = properties.color.red; elColorRed.value = properties.color.red;
elColorGreen.value = properties.color.green; elColorGreen.value = properties.color.green;
elColorBlue.value = properties.color.blue; elColorBlue.value = properties.color.blue;
elColorControl1.style.backgroundColor = elColorControl2.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; elColorControlVariant2.style.backgroundColor = "rgb(" + properties.color.red + "," +
properties.color.green + "," + properties.color.blue + ")";
} }
if (properties.type == "Model") { if (properties.type === "Model") {
elModelURL.value = properties.modelURL; elModelURL.value = properties.modelURL;
elShapeType.value = properties.shapeType; elShapeType.value = properties.shapeType;
setDropdownText(elShapeType); setDropdownText(elShapeType);
@ -998,24 +974,26 @@ function loaded() {
setTextareaScrolling(elModelTextures); setTextareaScrolling(elModelTextures);
elModelOriginalTextures.value = properties.originalTextures; elModelOriginalTextures.value = properties.originalTextures;
setTextareaScrolling(elModelOriginalTextures); setTextareaScrolling(elModelOriginalTextures);
} else if (properties.type == "Web") { } else if (properties.type === "Web") {
elWebSourceURL.value = properties.sourceUrl; elWebSourceURL.value = properties.sourceUrl;
elWebDPI.value = properties.dpi; elWebDPI.value = properties.dpi;
} else if (properties.type == "Text") { } else if (properties.type === "Text") {
elTextText.value = properties.text; elTextText.value = properties.text;
elTextLineHeight.value = properties.lineHeight.toFixed(4); elTextLineHeight.value = properties.lineHeight.toFixed(4);
elTextFaceCamera.checked = properties.faceCamera; elTextFaceCamera.checked = properties.faceCamera;
elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + properties.textColor.green + "," + properties.textColor.blue + ")"; elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," +
properties.textColor.green + "," + properties.textColor.blue + ")";
elTextTextColorRed.value = properties.textColor.red; elTextTextColorRed.value = properties.textColor.red;
elTextTextColorGreen.value = properties.textColor.green; elTextTextColorGreen.value = properties.textColor.green;
elTextTextColorBlue.value = properties.textColor.blue; elTextTextColorBlue.value = properties.textColor.blue;
elTextBackgroundColorRed.value = properties.backgroundColor.red; elTextBackgroundColorRed.value = properties.backgroundColor.red;
elTextBackgroundColorGreen.value = properties.backgroundColor.green; elTextBackgroundColorGreen.value = properties.backgroundColor.green;
elTextBackgroundColorBlue.value = properties.backgroundColor.blue; elTextBackgroundColorBlue.value = properties.backgroundColor.blue;
} else if (properties.type == "Light") { } else if (properties.type === "Light") {
elLightSpotLight.checked = properties.isSpotlight; elLightSpotLight.checked = properties.isSpotlight;
elLightColor.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; elLightColor.style.backgroundColor = "rgb(" + properties.color.red + "," +
properties.color.green + "," + properties.color.blue + ")";
elLightColorRed.value = properties.color.red; elLightColorRed.value = properties.color.red;
elLightColorGreen.value = properties.color.green; elLightColorGreen.value = properties.color.green;
elLightColorBlue.value = properties.color.blue; elLightColorBlue.value = properties.color.blue;
@ -1024,9 +1002,10 @@ function loaded() {
elLightFalloffRadius.value = properties.falloffRadius.toFixed(1); elLightFalloffRadius.value = properties.falloffRadius.toFixed(1);
elLightExponent.value = properties.exponent.toFixed(2); elLightExponent.value = properties.exponent.toFixed(2);
elLightCutoff.value = properties.cutoff.toFixed(2); elLightCutoff.value = properties.cutoff.toFixed(2);
} else if (properties.type == "Zone") { } else if (properties.type === "Zone") {
elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled; elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled;
elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," +
properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")";
elZoneKeyLightColorRed.value = properties.keyLight.color.red; elZoneKeyLightColorRed.value = properties.keyLight.color.red;
elZoneKeyLightColorGreen.value = properties.keyLight.color.green; elZoneKeyLightColorGreen.value = properties.keyLight.color.green;
elZoneKeyLightColorBlue.value = properties.keyLight.color.blue; elZoneKeyLightColorBlue.value = properties.keyLight.color.blue;
@ -1036,9 +1015,9 @@ function loaded() {
elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2);
elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL; elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL;
elZoneHazeModeInherit.checked = (properties.hazeMode == 'inherit'); elZoneHazeModeInherit.checked = (properties.hazeMode === 'inherit');
elZoneHazeModeDisabled.checked = (properties.hazeMode == 'disabled'); elZoneHazeModeDisabled.checked = (properties.hazeMode === 'disabled');
elZoneHazeModeEnabled.checked = (properties.hazeMode == 'enabled'); elZoneHazeModeEnabled.checked = (properties.hazeMode === 'enabled');
elZoneHazeRange.value = properties.haze.hazeRange.toFixed(0); elZoneHazeRange.value = properties.haze.hazeRange.toFixed(0);
elZoneHazeColor.style.backgroundColor = "rgb(" + elZoneHazeColor.style.backgroundColor = "rgb(" +
@ -1069,10 +1048,6 @@ function loaded() {
elZoneHazeBackgroundBlend.value = properties.haze.hazeBackgroundBlend.toFixed(2); elZoneHazeBackgroundBlend.value = properties.haze.hazeBackgroundBlend.toFixed(2);
// elZoneHazeAttenuateKeyLight.checked = properties.haze.hazeAttenuateKeyLight;
// elZoneHazeKeyLightRange.value = properties.haze.hazeKeyLightRange.toFixed(0);
// elZoneHazeKeyLightAltitude.value = properties.haze.hazeKeyLightAltitude.toFixed(0);
elZoneStageLatitude.value = properties.stage.latitude.toFixed(2); elZoneStageLatitude.value = properties.stage.latitude.toFixed(2);
elZoneStageLongitude.value = properties.stage.longitude.toFixed(2); elZoneStageLongitude.value = properties.stage.longitude.toFixed(2);
elZoneStageAltitude.value = properties.stage.altitude.toFixed(2); elZoneStageAltitude.value = properties.stage.altitude.toFixed(2);
@ -1085,7 +1060,8 @@ function loaded() {
elZoneBackgroundMode.value = properties.backgroundMode; elZoneBackgroundMode.value = properties.backgroundMode;
setDropdownText(elZoneBackgroundMode); setDropdownText(elZoneBackgroundMode);
elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red + "," + properties.skybox.color.green + "," + properties.skybox.color.blue + ")"; elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red + "," +
properties.skybox.color.green + "," + properties.skybox.color.blue + ")";
elZoneSkyboxColorRed.value = properties.skybox.color.red; elZoneSkyboxColorRed.value = properties.skybox.color.red;
elZoneSkyboxColorGreen.value = properties.skybox.color.green; elZoneSkyboxColorGreen.value = properties.skybox.color.green;
elZoneSkyboxColorBlue.value = properties.skybox.color.blue; elZoneSkyboxColorBlue.value = properties.skybox.color.blue;
@ -1095,8 +1071,9 @@ function loaded() {
elZoneGhostingAllowed.checked = properties.ghostingAllowed; elZoneGhostingAllowed.checked = properties.ghostingAllowed;
elZoneFilterURL.value = properties.filterURL; elZoneFilterURL.value = properties.filterURL;
showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox'); showElements(document.getElementsByClassName('skybox-section'),
} else if (properties.type == "PolyVox") { elZoneBackgroundMode.value === 'skybox');
} else if (properties.type === "PolyVox") {
elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2);
elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2);
elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2); elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2);
@ -1250,7 +1227,8 @@ function loaded() {
}); });
var numberListener = function (event) { var numberListener = function (event) {
userDataChanger("grabbableKey", event.target.getAttribute("data-user-data-type"), parseInt(event.target.value), elUserData, false); userDataChanger("grabbableKey",
event.target.getAttribute("data-user-data-type"), parseInt(event.target.value), elUserData, false);
}; };
elCloneableLifetime.addEventListener('change', numberListener); elCloneableLifetime.addEventListener('change', numberListener);
elCloneableLimit.addEventListener('change', numberListener); elCloneableLimit.addEventListener('change', numberListener);
@ -1279,7 +1257,7 @@ function loaded() {
showUserDataTextArea(); showUserDataTextArea();
showNewJSONEditorButton(); showNewJSONEditorButton();
hideSaveUserDataButton(); hideSaveUserDataButton();
updateProperty('userData', elUserData.value) updateProperty('userData', elUserData.value);
}); });
elSaveUserData.addEventListener("click", function() { elSaveUserData.addEventListener("click", function() {
@ -1303,24 +1281,6 @@ function loaded() {
elColorRed.addEventListener('change', colorChangeFunction); elColorRed.addEventListener('change', colorChangeFunction);
elColorGreen.addEventListener('change', colorChangeFunction); elColorGreen.addEventListener('change', colorChangeFunction);
elColorBlue.addEventListener('change', colorChangeFunction); elColorBlue.addEventListener('change', colorChangeFunction);
colorPickers.push($('#property-color-control1').colpick({
colorScheme: 'dark',
layout: 'hex',
color: '000000',
onShow: function(colpick) {
$('#property-color-control1').attr('active', 'true');
},
onHide: function(colpick) {
$('#property-color-control1').attr('active', 'false');
},
onSubmit: function(hsb, hex, rgb, el) {
$(el).css('background-color', '#' + hex);
$(el).colpickHide();
emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
// Keep the companion control in sync
elColorControl2.style.backgroundColor = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")";
}
}));
colorPickers.push($('#property-color-control2').colpick({ colorPickers.push($('#property-color-control2').colpick({
colorScheme: 'dark', colorScheme: 'dark',
layout: 'hex', layout: 'hex',
@ -1335,9 +1295,6 @@ function loaded() {
$(el).css('background-color', '#' + hex); $(el).css('background-color', '#' + hex);
$(el).colpickHide(); $(el).colpickHide();
emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
// Keep the companion control in sync
elColorControl1.style.backgroundColor = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")";
} }
})); }));
@ -1382,12 +1339,16 @@ function loaded() {
elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url')); elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url'));
elModelAnimationPlaying.addEventListener('change',createEmitGroupCheckedPropertyUpdateFunction('animation', 'running')); elModelAnimationPlaying.addEventListener('change',createEmitGroupCheckedPropertyUpdateFunction('animation', 'running'));
elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'fps')); elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'fps'));
elModelAnimationFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame')); elModelAnimationFrame.addEventListener('change',
elModelAnimationFirstFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame')); createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame'));
elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame')); elModelAnimationFirstFrame.addEventListener('change',
createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame'));
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')); elModelAnimationAllowTranslation.addEventListener('change',
createEmitGroupCheckedPropertyUpdateFunction('animation', 'allowTranslation'));
elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures')); elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
@ -1439,7 +1400,8 @@ function loaded() {
} }
})); }));
elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage', 'sunModelEnabled')); elZoneStageSunModelEnabled.addEventListener('change',
createEmitGroupCheckedPropertyUpdateFunction('stage', 'sunModelEnabled'));
colorPickers.push($('#property-zone-key-light-color').colpick({ colorPickers.push($('#property-zone-key-light-color').colpick({
colorScheme: 'dark', colorScheme: 'dark',
layout: 'hex', layout: 'hex',
@ -1456,18 +1418,26 @@ function loaded() {
emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight'); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight');
} }
})); }));
var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight', 'color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight', 'color',
elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue);
elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight', 'intensity')); elZoneKeyLightIntensity.addEventListener('change',
elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight', 'ambientIntensity')); createEmitGroupNumberPropertyUpdateFunction('keyLight', 'intensity'));
elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight', 'ambientURL')); elZoneKeyLightAmbientIntensity.addEventListener('change',
var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight', 'direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY); createEmitGroupNumberPropertyUpdateFunction('keyLight', 'ambientIntensity'));
elZoneKeyLightAmbientURL.addEventListener('change',
createEmitGroupTextPropertyUpdateFunction('keyLight', 'ambientURL'));
var zoneKeyLightDirectionChangeFunction =
createEmitGroupVec3PropertyUpdateFunction('keyLight', 'direction',
elZoneKeyLightDirectionX, elZoneKeyLightDirectionY);
elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
var hazeModeChanged = createZoneComponentModeChangedFunction('hazeMode', elZoneHazeModeInherit, elZoneHazeModeDisabled, elZoneHazeModeEnabled) var hazeModeChanged =
createZoneComponentModeChangedFunction('hazeMode', elZoneHazeModeInherit,
elZoneHazeModeDisabled, elZoneHazeModeEnabled);
elZoneHazeModeInherit.addEventListener('change', hazeModeChanged); elZoneHazeModeInherit.addEventListener('change', hazeModeChanged);
elZoneHazeModeDisabled.addEventListener('change', hazeModeChanged); elZoneHazeModeDisabled.addEventListener('change', hazeModeChanged);
elZoneHazeModeEnabled.addEventListener('change', hazeModeChanged); elZoneHazeModeEnabled.addEventListener('change', hazeModeChanged);
@ -1524,23 +1494,23 @@ function loaded() {
elZoneHazeGlareColorGreen.addEventListener('change', zoneHazeGlareColorChangeFunction); elZoneHazeGlareColorGreen.addEventListener('change', zoneHazeGlareColorChangeFunction);
elZoneHazeGlareColorBlue.addEventListener('change', zoneHazeGlareColorChangeFunction); elZoneHazeGlareColorBlue.addEventListener('change', zoneHazeGlareColorChangeFunction);
elZoneHazeEnableGlare.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('haze', 'hazeEnableGlare')); elZoneHazeEnableGlare.addEventListener('change',
createEmitGroupCheckedPropertyUpdateFunction('haze', 'hazeEnableGlare'));
elZonehazeGlareAngle.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeGlareAngle')); elZonehazeGlareAngle.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeGlareAngle'));
elZoneHazeAltitudeEffect.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('haze', 'hazeAltitudeEffect')); elZoneHazeAltitudeEffect.addEventListener('change',
createEmitGroupCheckedPropertyUpdateFunction('haze', 'hazeAltitudeEffect'));
elZoneHazeCeiling.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeCeiling')); elZoneHazeCeiling.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeCeiling'));
elZoneHazeBaseRef.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeBaseRef')); elZoneHazeBaseRef.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeBaseRef'));
elZoneHazeBackgroundBlend.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeBackgroundBlend')); elZoneHazeBackgroundBlend.addEventListener('change',
createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeBackgroundBlend'));
// elZoneHazeAttenuateKeyLight.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('haze', 'hazeAttenuateKeyLight'));
// elZoneHazeKeyLightRange.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeKeyLightRange'));
// elZoneHazeKeyLightAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeKeyLightAltitude'));
elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'latitude')); elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'latitude'));
elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'longitude')); elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'longitude'));
elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'altitude')); elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'altitude'));
elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage', 'automaticHourDay')); elZoneStageAutomaticHourDay.addEventListener('change',
createEmitGroupCheckedPropertyUpdateFunction('stage', 'automaticHourDay'));
elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'day')); elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'day'));
elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'hour')); elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage', 'hour'));
@ -1587,26 +1557,26 @@ function loaded() {
elMoveSelectionToGrid.addEventListener("click", function() { elMoveSelectionToGrid.addEventListener("click", function() {
EventBridge.emitWebEvent(JSON.stringify({ EventBridge.emitWebEvent(JSON.stringify({
type: "action", type: "action",
action: "moveSelectionToGrid", action: "moveSelectionToGrid"
})); }));
}); });
elMoveAllToGrid.addEventListener("click", function() { elMoveAllToGrid.addEventListener("click", function() {
EventBridge.emitWebEvent(JSON.stringify({ EventBridge.emitWebEvent(JSON.stringify({
type: "action", type: "action",
action: "moveAllToGrid", action: "moveAllToGrid"
})); }));
}); });
elResetToNaturalDimensions.addEventListener("click", function() { elResetToNaturalDimensions.addEventListener("click", function() {
EventBridge.emitWebEvent(JSON.stringify({ EventBridge.emitWebEvent(JSON.stringify({
type: "action", type: "action",
action: "resetToNaturalDimensions", action: "resetToNaturalDimensions"
})); }));
}); });
elRescaleDimensionsButton.addEventListener("click", function() { elRescaleDimensionsButton.addEventListener("click", function() {
EventBridge.emitWebEvent(JSON.stringify({ EventBridge.emitWebEvent(JSON.stringify({
type: "action", type: "action",
action: "rescaleDimensions", action: "rescaleDimensions",
percentage: parseFloat(elRescaleDimensionsPct.value), percentage: parseFloat(elRescaleDimensionsPct.value)
})); }));
}); });
elReloadScriptsButton.addEventListener("click", function() { elReloadScriptsButton.addEventListener("click", function() {
@ -1638,7 +1608,7 @@ function loaded() {
var ev = document.createEvent("HTMLEvents"); var ev = document.createEvent("HTMLEvents");
ev.initEvent("change", true, true); ev.initEvent("change", true, true);
document.activeElement.dispatchEvent(ev); document.activeElement.dispatchEvent(ev);
} };
// For input and textarea elements, select all of the text on focus // For input and textarea elements, select all of the text on focus
// WebKit-based browsers, such as is used with QWebView, have a quirk // WebKit-based browsers, such as is used with QWebView, have a quirk
@ -1653,13 +1623,17 @@ function loaded() {
for (var i = 0; i < els.length; i++) { for (var i = 0; i < els.length; i++) {
var clicked = false; var clicked = false;
var originalText; var originalText;
// TODO FIXME: (JSHint) Functions declared within loops referencing
// an outer scoped variable may lead to confusing semantics.
els[i].onfocus = function(e) { els[i].onfocus = function(e) {
originalText = this.value; originalText = this.value;
this.select(); this.select();
clicked = false; clicked = false;
}; };
// TODO FIXME: (JSHint) Functions declared within loops referencing
// an outer scoped variable may lead to confusing semantics.
els[i].onmouseup = function(e) { els[i].onmouseup = function(e) {
if (!clicked && originalText == this.value) { if (!clicked && originalText === this.value) {
e.preventDefault(); e.preventDefault();
} }
clicked = true; clicked = true;
@ -1674,15 +1648,15 @@ function loaded() {
var toggleCollapsedEvent = function(event) { var toggleCollapsedEvent = function(event) {
var element = event.target.parentNode.parentNode; var element = event.target.parentNode.parentNode;
var isCollapsed = element.dataset.collapsed !== "true"; var isCollapsed = element.dataset.collapsed !== "true";
element.dataset.collapsed = isCollapsed ? "true" : false element.dataset.collapsed = isCollapsed ? "true" : false;
element.setAttribute("collapsed", isCollapsed ? "true" : "false"); element.setAttribute("collapsed", isCollapsed ? "true" : "false");
element.getElementsByTagName("span")[0].textContent = isCollapsed ? "L" : "M"; element.getElementsByTagName("span")[0].textContent = isCollapsed ? "L" : "M";
}; };
for (var i = 0, length = elCollapsible.length; i < length; i++) { for (var collapseIndex = 0, numCollapsibles = elCollapsible.length; collapseIndex < numCollapsibles; ++collapseIndex) {
var element = elCollapsible[i]; var curCollapsibleElement = elCollapsible[collapseIndex];
element.addEventListener("click", toggleCollapsedEvent, true); curCollapsibleElement.addEventListener("click", toggleCollapsedEvent, true);
}; }
// Textarea scrollbars // Textarea scrollbars
@ -1690,17 +1664,17 @@ function loaded() {
var textareaOnChangeEvent = function(event) { var textareaOnChangeEvent = function(event) {
setTextareaScrolling(event.target); setTextareaScrolling(event.target);
} };
for (var i = 0, length = elTextareas.length; i < length; i++) { for (var textAreaIndex = 0, numTextAreas = elTextareas.length; textAreaIndex < numTextAreas; ++textAreaIndex) {
var element = elTextareas[i]; var curTextAreaElement = elTextareas[textAreaIndex];
setTextareaScrolling(element); setTextareaScrolling(curTextAreaElement);
element.addEventListener("input", textareaOnChangeEvent, false); curTextAreaElement.addEventListener("input", textareaOnChangeEvent, false);
element.addEventListener("change", textareaOnChangeEvent, false); curTextAreaElement.addEventListener("change", textareaOnChangeEvent, false);
/* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize
event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */ event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */
element.addEventListener("mouseup", textareaOnChangeEvent, false); curTextAreaElement.addEventListener("mouseup", textareaOnChangeEvent, false);
}; }
// Dropdowns // Dropdowns
// For each dropdown the following replacement is created in place of the oriringal dropdown... // For each dropdown the following replacement is created in place of the oriringal dropdown...
@ -1749,22 +1723,23 @@ function loaded() {
} }
var elDropdowns = document.getElementsByTagName("select"); var elDropdowns = document.getElementsByTagName("select");
for (var i = 0; i < elDropdowns.length; i++) { for (var dropDownIndex = 0; dropDownIndex < elDropdowns.length; ++dropDownIndex) {
var options = elDropdowns[i].getElementsByTagName("option"); var options = elDropdowns[dropDownIndex].getElementsByTagName("option");
var selectedOption = 0; var selectedOption = 0;
for (var j = 0; j < options.length; j++) { for (var optionIndex = 0; optionIndex < options.length; ++optionIndex) {
if (options[j].getAttribute("selected") === "selected") { if (options[optionIndex].getAttribute("selected") === "selected") {
selectedOption = j; selectedOption = optionIndex;
// TODO: Shouldn't there be a break here?
} }
} }
var div = elDropdowns[i].parentNode; var div = elDropdowns[dropDownIndex].parentNode;
var dl = document.createElement("dl"); var dl = document.createElement("dl");
div.appendChild(dl); div.appendChild(dl);
var dt = document.createElement("dt"); var dt = document.createElement("dt");
dt.name = elDropdowns[i].name; dt.name = elDropdowns[dropDownIndex].name;
dt.id = elDropdowns[i].id; dt.id = elDropdowns[dropDownIndex].id;
dt.addEventListener("click", toggleDropdown, true); dt.addEventListener("click", toggleDropdown, true);
dl.appendChild(dt); dl.appendChild(dt);
@ -1773,9 +1748,9 @@ function loaded() {
span.textContent = options[selectedOption].firstChild.textContent; span.textContent = options[selectedOption].firstChild.textContent;
dt.appendChild(span); dt.appendChild(span);
var span = document.createElement("span"); var spanCaratDown = document.createElement("span");
span.textContent = "5"; // caratDn spanCaratDown.textContent = "5"; // caratDn
dt.appendChild(span); dt.appendChild(spanCaratDown);
var dd = document.createElement("dd"); var dd = document.createElement("dd");
dl.appendChild(dd); dl.appendChild(dd);
@ -1783,10 +1758,10 @@ function loaded() {
var ul = document.createElement("ul"); var ul = document.createElement("ul");
dd.appendChild(ul); dd.appendChild(ul);
for (var j = 0; j < options.length; j++) { for (var listOptionIndex = 0; listOptionIndex < options.length; ++listOptionIndex) {
var li = document.createElement("li"); var li = document.createElement("li");
li.setAttribute("value", options[j].value); li.setAttribute("value", options[listOptionIndex].value);
li.textContent = options[j].firstChild.textContent; li.textContent = options[listOptionIndex].firstChild.textContent;
li.addEventListener("click", setDropdownValue); li.addEventListener("click", setDropdownValue);
ul.appendChild(li); ul.appendChild(li);
} }

View file

@ -15,6 +15,9 @@ function setUpKeyboardControl() {
var KEYBOARD_HEIGHT = 200; var KEYBOARD_HEIGHT = 200;
function raiseKeyboard() { function raiseKeyboard() {
window.isKeyboardRaised = true;
window.isNumericKeyboard = this.type === "number";
if (lowerTimer !== null) { if (lowerTimer !== null) {
clearTimeout(lowerTimer); clearTimeout(lowerTimer);
lowerTimer = null; lowerTimer = null;
@ -35,6 +38,9 @@ function setUpKeyboardControl() {
} }
function doLowerKeyboard() { function doLowerKeyboard() {
window.isKeyboardRaised = false;
window.isNumericKeyboard = false;
EventBridge.emitWebEvent("_LOWER_KEYBOARD"); EventBridge.emitWebEvent("_LOWER_KEYBOARD");
lowerTimer = null; lowerTimer = null;
isRaised = false; isRaised = false;

View file

@ -110,8 +110,10 @@
var filterText; // Used for updating Purchases QML var filterText; // Used for updating Purchases QML
function onScreenChanged(type, url) { function onScreenChanged(type, url) {
onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1;
onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH_BASE) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1); onWalletScreen = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1;
wireEventBridge(onCommerceScreen); onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH_BASE) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH
|| url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1);
wireEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen);
if (url === MARKETPLACE_PURCHASES_QML_PATH) { if (url === MARKETPLACE_PURCHASES_QML_PATH) {
tablet.sendToQml({ tablet.sendToQml({
@ -122,7 +124,7 @@
} }
// for toolbar mode: change button to active when window is first openend, false otherwise. // for toolbar mode: change button to active when window is first openend, false otherwise.
marketplaceButton.editProperties({ isActive: onMarketplaceScreen || onCommerceScreen }); marketplaceButton.editProperties({ isActive: (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen });
if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) {
ContextOverlay.isInMarketplaceInspectionMode = true; ContextOverlay.isInMarketplaceInspectionMode = true;
} else { } else {
@ -322,6 +324,11 @@
} else if (parsedJsonMessage.type === "LOGIN") { } else if (parsedJsonMessage.type === "LOGIN") {
openLoginWindow(); openLoginWindow();
} else if (parsedJsonMessage.type === "WALLET_SETUP") { } else if (parsedJsonMessage.type === "WALLET_SETUP") {
wireEventBridge(true);
tablet.sendToQml({
method: 'updateWalletReferrer',
referrer: "marketplace cta"
});
openWallet(); openWallet();
} else if (parsedJsonMessage.type === "MY_ITEMS") { } else if (parsedJsonMessage.type === "MY_ITEMS") {
referrerURL = MARKETPLACE_URL_INITIAL; referrerURL = MARKETPLACE_URL_INITIAL;
@ -399,6 +406,7 @@
referrer: "purchases" referrer: "purchases"
}); });
openWallet(); openWallet();
break;
case 'checkout_walletNotSetUp': case 'checkout_walletNotSetUp':
wireEventBridge(true); wireEventBridge(true);
tablet.sendToQml({ tablet.sendToQml({