mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-17 02:01:48 +02:00
Merge branch 'master' into NOverlays3
This commit is contained in:
commit
4c5ff0dc3e
36 changed files with 1238 additions and 576 deletions
20
BUILD.md
20
BUILD.md
|
@ -13,7 +13,7 @@
|
|||
|
||||
### CMake External Project Dependencies
|
||||
|
||||
These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required.
|
||||
These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required.
|
||||
- [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83
|
||||
- [glm](https://glm.g-truc.net/0.9.8/index.html): 0.9.8
|
||||
- [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Win32) / 0.5 (Mac)
|
||||
|
@ -22,7 +22,7 @@ These dependencies need not be installed manually. They are automatically downlo
|
|||
- [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3
|
||||
- [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3
|
||||
- [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/): 4.3
|
||||
- [vcpkg](https://github.com/highfidelity/vcpkg):
|
||||
- [vcpkg](https://github.com/highfidelity/vcpkg):
|
||||
- [VHACD](https://github.com/virneo/v-hacd)
|
||||
- [zlib](http://www.zlib.net/): 1.28 (Win32 only)
|
||||
- [nvtt](https://github.com/highfidelity/nvidia-texture-tools): 2.1.1 (customized)
|
||||
|
@ -48,6 +48,18 @@ The path it needs to be set to will depend on where and how Qt5 was installed. e
|
|||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.10.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
|
||||
|
||||
#### Vcpkg
|
||||
|
||||
Hifi uses vcpkg to download and build dependencies.
|
||||
You do not need to install vcpkg.
|
||||
|
||||
Building the dependencies can be lengthy and the resulting files will be stored in your OS temp directory.
|
||||
However, those files can potentially get cleaned up by the OS, so in order to avoid this and having to redo the lengthy build step, you can set the following environment variable:
|
||||
|
||||
export HIFI_VCPKG_BASE=/path/to/directory
|
||||
|
||||
Where /path/to/directory is the path to a directory where you wish the build files to get stored.
|
||||
|
||||
#### Generating build files
|
||||
|
||||
Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean.
|
||||
|
@ -80,7 +92,7 @@ In the examples below the variable $NAME would be replaced by the name of the de
|
|||
|
||||
### Optional Components
|
||||
|
||||
#### Build Options
|
||||
#### Build Options
|
||||
|
||||
The following build options can be used when running CMake
|
||||
|
||||
|
@ -89,7 +101,7 @@ The following build options can be used when running CMake
|
|||
* BUILD_TESTS
|
||||
* BUILD_TOOLS
|
||||
|
||||
#### Developer Build Options
|
||||
#### Developer Build Options
|
||||
|
||||
* USE_GLES
|
||||
* DISABLE_UI
|
||||
|
|
23
BUILD_WIN.md
23
BUILD_WIN.md
|
@ -19,7 +19,7 @@ If you do not wish to use the Python installation bundled with Visual Studio, yo
|
|||
|
||||
### Step 2. Installing CMake
|
||||
|
||||
Download and install the latest version of CMake 3.9.
|
||||
Download and install the latest version of CMake 3.9.
|
||||
|
||||
Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.9 Version page](https://cmake.org/files/v3.9/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted.
|
||||
|
||||
|
@ -35,20 +35,7 @@ Go to `Control Panel > System > Advanced System Settings > Environment Variables
|
|||
* Set "Variable name": `QT_CMAKE_PREFIX_PATH`
|
||||
* Set "Variable value": `C:\Qt\5.10.1\msvc2017_64\lib\cmake`
|
||||
|
||||
### Step 5. Installing [vcpkg](https://github.com/Microsoft/vcpkg)
|
||||
|
||||
* Clone the VCPKG [repository](https://github.com/Microsoft/vcpkg)
|
||||
* Follow the instructions in the [readme](https://github.com/Microsoft/vcpkg/blob/master/README.md) to bootstrap vcpkg
|
||||
* Note, you may need to do these in a _Developer Command Prompt_
|
||||
* Set an environment variable VCPKG_ROOT to the location of the cloned repository
|
||||
* Close and re-open any command prompts after setting the environment variable so that they will pick up the change
|
||||
|
||||
### Step 6. Installing OpenSSL via vcpkg
|
||||
|
||||
* In the vcpkg directory, install the 64 bit OpenSSL package with the command `.\vcpkg install openssl:x64-windows`
|
||||
* Once the build completes you should have a file `ssl.h` in `${VCPKG_ROOT}/installed/x64-windows/include/openssl`
|
||||
|
||||
### Step 7. Running CMake to Generate Build Files
|
||||
### Step 5. Running CMake to Generate Build Files
|
||||
|
||||
Run Command Prompt from Start and run the following commands:
|
||||
```
|
||||
|
@ -58,9 +45,9 @@ cd build
|
|||
cmake .. -G "Visual Studio 15 Win64"
|
||||
```
|
||||
|
||||
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
|
||||
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
|
||||
|
||||
### Step 8. Making a Build
|
||||
### Step 6. Making a Build
|
||||
|
||||
Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio.
|
||||
|
||||
|
@ -68,7 +55,7 @@ Change the Solution Configuration (menu ribbon under the menu bar, next to the g
|
|||
|
||||
Run from the menu bar `Build > Build Solution`.
|
||||
|
||||
### Step 9. Testing Interface
|
||||
### Step 7. Testing Interface
|
||||
|
||||
Create another environment variable (see Step #4)
|
||||
* Set "Variable name": `_NO_DEBUG_HEAP`
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
#include <AnimUtil.h>
|
||||
#include <ClientTraitsHandler.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <ResourceRequestObserver.h>
|
||||
#include <AvatarLogging.h>
|
||||
|
||||
|
||||
ScriptableAvatar::ScriptableAvatar() {
|
||||
_clientTraitsHandler.reset(new ClientTraitsHandler(this));
|
||||
|
@ -62,11 +65,28 @@ AnimationDetails ScriptableAvatar::getAnimationDetails() {
|
|||
return _animationDetails;
|
||||
}
|
||||
|
||||
int ScriptableAvatar::getJointIndex(const QString& name) const {
|
||||
// Faux joints:
|
||||
int result = AvatarData::getJointIndex(name);
|
||||
if (result != -1) {
|
||||
return result;
|
||||
}
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
return _fstJointIndices.value(name) - 1;
|
||||
}
|
||||
|
||||
QStringList ScriptableAvatar::getJointNames() const {
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
return _fstJointNames;
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||
_bind.reset();
|
||||
_animSkeleton.reset();
|
||||
|
||||
AvatarData::setSkeletonModelURL(skeletonModelURL);
|
||||
updateJointMappings();
|
||||
}
|
||||
|
||||
static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, const glm::vec3 translation) {
|
||||
|
@ -77,10 +97,6 @@ static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation,
|
|||
}
|
||||
|
||||
void ScriptableAvatar::update(float deltatime) {
|
||||
if (_bind.isNull() && !_skeletonFBXURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton.
|
||||
_bind = DependencyManager::get<AnimationCache>()->getAnimation(_skeletonFBXURL);
|
||||
}
|
||||
|
||||
// Run animation
|
||||
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) {
|
||||
if (!_animSkeleton) {
|
||||
|
@ -146,6 +162,82 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
_clientTraitsHandler->sendChangedTraitsToMixer();
|
||||
}
|
||||
|
||||
void ScriptableAvatar::updateJointMappings() {
|
||||
{
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
_fstJointIndices.clear();
|
||||
_fstJointNames.clear();
|
||||
_jointData.clear();
|
||||
}
|
||||
|
||||
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
|
||||
////
|
||||
// TODO: Should we rely upon HTTPResourceRequest for ResourceRequestObserver instead?
|
||||
// HTTPResourceRequest::doSend() covers all of the following and
|
||||
// then some. It doesn't cover the connect() call, so we may
|
||||
// want to add a HTTPResourceRequest::doSend() method that does
|
||||
// connects.
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
DependencyManager::get<ResourceRequestObserver>()->update(
|
||||
_skeletonModelURL, -1, "AvatarData::updateJointMappings");
|
||||
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
|
||||
//
|
||||
////
|
||||
connect(networkReply, &QNetworkReply::finished, this, &ScriptableAvatar::setJointMappingsFromNetworkReply);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptableAvatar::setJointMappingsFromNetworkReply() {
|
||||
QNetworkReply* networkReply = static_cast<QNetworkReply*>(sender());
|
||||
// before we process this update, make sure that the skeleton model URL hasn't changed
|
||||
// since we made the FST request
|
||||
if (networkReply->url() != _skeletonModelURL) {
|
||||
qCDebug(avatars) << "Refusing to set joint mappings for FST URL that does not match the current URL";
|
||||
networkReply->deleteLater();
|
||||
return;
|
||||
}
|
||||
{
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
QByteArray line;
|
||||
while (!(line = networkReply->readLine()).isEmpty()) {
|
||||
line = line.trimmed();
|
||||
if (line.startsWith("filename")) {
|
||||
int filenameIndex = line.indexOf('=') + 1;
|
||||
if (filenameIndex > 0) {
|
||||
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
|
||||
}
|
||||
}
|
||||
if (!line.startsWith("jointIndex")) {
|
||||
continue;
|
||||
}
|
||||
int jointNameIndex = line.indexOf('=') + 1;
|
||||
if (jointNameIndex == 0) {
|
||||
continue;
|
||||
}
|
||||
int secondSeparatorIndex = line.indexOf('=', jointNameIndex);
|
||||
if (secondSeparatorIndex == -1) {
|
||||
continue;
|
||||
}
|
||||
QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed();
|
||||
bool ok;
|
||||
int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok);
|
||||
if (ok) {
|
||||
while (_fstJointNames.size() < jointIndex + 1) {
|
||||
_fstJointNames.append(QString());
|
||||
}
|
||||
_fstJointNames[jointIndex] = jointName;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _fstJointNames.size(); i++) {
|
||||
_fstJointIndices.insert(_fstJointNames.at(i), i + 1);
|
||||
}
|
||||
}
|
||||
networkReply->deleteLater();
|
||||
}
|
||||
|
||||
void ScriptableAvatar::setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement) {
|
||||
_headData->setHasProceduralBlinkFaceMovement(hasProceduralBlinkFaceMovement);
|
||||
}
|
||||
|
|
|
@ -153,6 +153,27 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetails();
|
||||
|
||||
/**jsdoc
|
||||
* Get the names of all the joints in the current avatar.
|
||||
* @function MyAvatar.getJointNames
|
||||
* @returns {string[]} The joint names.
|
||||
* @example <caption>Report the names of all the joints in your current avatar.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointNames()));
|
||||
*/
|
||||
Q_INVOKABLE virtual QStringList getJointNames() const override;
|
||||
|
||||
/**jsdoc
|
||||
* Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by
|
||||
* {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}.
|
||||
* @function MyAvatar.getJointIndex
|
||||
* @param {string} name - The name of the joint.
|
||||
* @returns {number} The index of the joint.
|
||||
* @example <caption>Report the index of your avatar's left arm joint.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointIndex("LeftArm"));
|
||||
*/
|
||||
/// Returns the index of the joint with the specified name, or -1 if not found/unknown.
|
||||
Q_INVOKABLE virtual int getJointIndex(const QString& name) const override;
|
||||
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
|
||||
virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking = false) override;
|
||||
|
@ -167,12 +188,23 @@ public:
|
|||
public slots:
|
||||
void update(float deltatime);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setJointMappingsFromNetworkReply
|
||||
*/
|
||||
void setJointMappingsFromNetworkReply();
|
||||
|
||||
private:
|
||||
AnimationPointer _animation;
|
||||
AnimationDetails _animationDetails;
|
||||
QStringList _maskedJoints;
|
||||
AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies
|
||||
std::shared_ptr<AnimSkeleton> _animSkeleton;
|
||||
QHash<QString, int> _fstJointIndices; ///< 1-based, since zero is returned for missing keys
|
||||
QStringList _fstJointNames; ///< in order of depth-first traversal
|
||||
QUrl _skeletonFBXURL;
|
||||
|
||||
/// Loads the joint indices, names from the FST file (if any)
|
||||
void updateJointMappings();
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptableAvatar_h
|
||||
|
|
|
@ -364,7 +364,7 @@ function validateInputs() {
|
|||
if (keyVal.length === 0) {
|
||||
empty = true
|
||||
|
||||
markParentRowInvalid(input);
|
||||
markParentRowInvalid(input)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -373,11 +373,13 @@ function validateInputs() {
|
|||
_.each(otherKeys, function(otherKeyCell) {
|
||||
var keyInput = $(otherKeyCell).children('input');
|
||||
|
||||
var lowerNewValue = keyVal.toLowerCase();
|
||||
|
||||
if (keyInput.length) {
|
||||
if ($(keyInput).val() == keyVal) {
|
||||
if ($(keyInput).val().toLowerCase() == lowerNewValue) {
|
||||
duplicateKey = true;
|
||||
}
|
||||
} else if ($(otherKeyCell).html() == keyVal) {
|
||||
} else if ($(otherKeyCell).html().toLowerCase() == lowerNewValue) {
|
||||
duplicateKey = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,6 @@ $(document).ready(function(){
|
|||
Settings.extraGroupsAtIndex = Settings.extraDomainGroupsAtIndex;
|
||||
|
||||
Settings.afterReloadActions = function() {
|
||||
// append the domain selection modal
|
||||
appendDomainIDButtons();
|
||||
|
||||
// call our method to setup the HF account button
|
||||
setupHFAccountButton();
|
||||
|
||||
|
@ -52,6 +49,11 @@ $(document).ready(function(){
|
|||
if (cloudWizardExit != undefined) {
|
||||
$('#cloud-domains-alert').show();
|
||||
}
|
||||
|
||||
$(Settings.DOMAIN_ID_SELECTOR).siblings('span').append("</br><strong>Changing the domain ID for a Cloud Domain may result in an incorrect status for the domain on your Cloud Domains page.</strong>");
|
||||
} else {
|
||||
// append the domain selection modal
|
||||
appendDomainIDButtons();
|
||||
}
|
||||
|
||||
handleAction();
|
||||
|
@ -59,9 +61,9 @@ $(document).ready(function(){
|
|||
|
||||
Settings.handlePostSettings = function(formJSON) {
|
||||
|
||||
if (!verifyAvatarHeights()) {
|
||||
return false;
|
||||
}
|
||||
if (!verifyAvatarHeights()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we've set the basic http password
|
||||
if (formJSON["security"]) {
|
||||
|
|
|
@ -3172,24 +3172,34 @@ void DomainServer::processPathQueryPacket(QSharedPointer<ReceivedMessage> messag
|
|||
const QString PATH_VIEWPOINT_KEY = "viewpoint";
|
||||
const QString INDEX_PATH = "/";
|
||||
|
||||
// check out paths in the _configMap to see if we have a match
|
||||
auto keypath = QString(PATHS_SETTINGS_KEYPATH_FORMAT).arg(SETTINGS_PATHS_KEY).arg(pathQuery);
|
||||
QVariant pathMatch = _settingsManager.valueForKeyPath(keypath);
|
||||
QString responseViewpoint;
|
||||
|
||||
if (pathMatch.isValid() || pathQuery == INDEX_PATH) {
|
||||
// check out paths in the _configMap to see if we have a match
|
||||
auto pathsVariant = _settingsManager.valueForKeyPath(SETTINGS_PATHS_KEY);
|
||||
|
||||
auto lowerPathQuery = pathQuery.toLower();
|
||||
|
||||
if (pathsVariant.canConvert<QVariantMap>()) {
|
||||
auto pathsMap = pathsVariant.toMap();
|
||||
|
||||
// enumerate the paths and look case-insensitively for a matching one
|
||||
for (auto it = pathsMap.constKeyValueBegin(); it != pathsMap.constKeyValueEnd(); ++it) {
|
||||
if ((*it).first.toLower() == lowerPathQuery) {
|
||||
responseViewpoint = (*it).second.toMap()[PATH_VIEWPOINT_KEY].toString().toLower();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (responseViewpoint.isEmpty() && pathQuery == INDEX_PATH) {
|
||||
const QString DEFAULT_INDEX_PATH = "/0,0,0/0,0,0,1";
|
||||
responseViewpoint = DEFAULT_INDEX_PATH;
|
||||
}
|
||||
|
||||
if (!responseViewpoint.isEmpty()) {
|
||||
// we got a match, respond with the resulting viewpoint
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
QString responseViewpoint;
|
||||
|
||||
// if we didn't match the path BUT this is for the index path then send back our default
|
||||
if (pathMatch.isValid()) {
|
||||
responseViewpoint = pathMatch.toMap()[PATH_VIEWPOINT_KEY].toString();
|
||||
} else {
|
||||
const QString DEFAULT_INDEX_PATH = "/0,0,0/0,0,0,1";
|
||||
responseViewpoint = DEFAULT_INDEX_PATH;
|
||||
}
|
||||
|
||||
if (!responseViewpoint.isEmpty()) {
|
||||
QByteArray viewpointUTF8 = responseViewpoint.toUtf8();
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ Item {
|
|||
property bool isCurrentlySendingAsset: false;
|
||||
property string assetName: "";
|
||||
property string assetCertID: "";
|
||||
property string couponID: "";
|
||||
property string authorizationID: "";
|
||||
property string sendingPubliclyEffectImage;
|
||||
property var http;
|
||||
property var listModelName;
|
||||
|
@ -108,6 +110,27 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
onAuthorizeAssetTransferResult: {
|
||||
if (!root.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
root.isCurrentlySendingAsset = false;
|
||||
|
||||
if (result.status === 'success') {
|
||||
root.authorizationID = result.data.authorization_id;
|
||||
authorizationIDText.text = root.authorizationID;
|
||||
root.couponID = result.data.coupon_id;
|
||||
couponIDText.text = root.couponID
|
||||
if (couponIDTextField.text !== root.couponID) {
|
||||
console.log("SendAsset: Returned coupon ID doesn't match client-generated coupon ID!");
|
||||
}
|
||||
root.nextActiveView = 'paymentSuccess';
|
||||
} else {
|
||||
root.nextActiveView = 'paymentFailure';
|
||||
}
|
||||
}
|
||||
|
||||
onCertificateInfoResult: {
|
||||
if (result.status !== 'success') {
|
||||
console.log("Failed to get certificate info", result.data.message);
|
||||
|
@ -269,7 +292,7 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: sendAssetText;
|
||||
text: root.assetCertID === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:";
|
||||
text: root.assetCertID === "" ? "Send Money To:" : "Send \"" + root.assetName + "\" To:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 26;
|
||||
|
@ -370,6 +393,51 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: createCouponButton;
|
||||
// Anchors
|
||||
anchors.top: nearbyButton.bottom;
|
||||
anchors.topMargin: 32;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
height: connectionButton.height;
|
||||
width: connectionButton.width;
|
||||
|
||||
Image {
|
||||
anchors.top: parent.top;
|
||||
source: "./images/coupon.svg";
|
||||
height: 70;
|
||||
width: parent.width;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
horizontalAlignment: Image.AlignHCenter;
|
||||
verticalAlignment: Image.AlignTop;
|
||||
mipmap: true;
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
text: "Create Coupon";
|
||||
// Anchors
|
||||
anchors.bottom: parent.bottom;
|
||||
height: 15;
|
||||
width: parent.width;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
sendAssetStep.referrer = "createCoupon";
|
||||
sendAssetStep.selectedRecipientNodeID = "";
|
||||
couponIDTextField.text = generateRandomCouponID();
|
||||
|
||||
root.nextActiveView = "sendAssetStep";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: backButton_sendAssetHome;
|
||||
visible: parentAppNavBarHeight === 0;
|
||||
|
@ -860,7 +928,7 @@ Item {
|
|||
id: sendAssetStep;
|
||||
z: 996;
|
||||
|
||||
property string referrer; // either "connections", "nearby", or "payIn"
|
||||
property string referrer; // either "connections", "nearby", "payIn", or "createCoupon"
|
||||
property string selectedRecipientNodeID;
|
||||
property string selectedRecipientDisplayName;
|
||||
property string selectedRecipientUserName;
|
||||
|
@ -872,7 +940,8 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: sendAssetText_sendAssetStep;
|
||||
text: sendAssetStep.referrer === "payIn" && root.assetCertID !== "" ? "Send \"" + root.assetName + "\":" :
|
||||
text: ((sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "createCoupon") &&
|
||||
root.assetCertID !== "") ? "Send \"" + root.assetName + "\":" :
|
||||
(root.assetCertID === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:");
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
|
@ -901,12 +970,13 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: sendToText_sendAssetStep;
|
||||
text: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") ? "Send to:" : "Gift to:";
|
||||
text: sendAssetStep.referrer === "createCoupon" ? "Coupon ID:" :
|
||||
(root.assetCertID === "" || sendAssetStep.referrer === "payIn") ? "Send to:" : "Gift to:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: 90;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
|
@ -915,8 +985,10 @@ Item {
|
|||
}
|
||||
|
||||
RecipientDisplay {
|
||||
visible: sendAssetStep.referrer !== "createCoupon";
|
||||
anchors.top: parent.top;
|
||||
anchors.left: sendToText_sendAssetStep.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: changeButton.left;
|
||||
anchors.rightMargin: 12;
|
||||
height: parent.height;
|
||||
|
@ -929,6 +1001,68 @@ Item {
|
|||
multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
|
||||
}
|
||||
|
||||
Item {
|
||||
id: couponIDContainer;
|
||||
visible: sendAssetStep.referrer === "createCoupon";
|
||||
anchors.top: parent.top;
|
||||
anchors.left: sendToText_sendAssetStep.right;
|
||||
anchors.right: parent.right;
|
||||
height: parent.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: couponIDHelp;
|
||||
text: "[?]";
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 8;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
height: 30;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.blueAccent;
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: true;
|
||||
onEntered: {
|
||||
parent.color = hifi.colors.blueHighlight;
|
||||
}
|
||||
onExited: {
|
||||
parent.color = hifi.colors.blueAccent;
|
||||
}
|
||||
onClicked: {
|
||||
lightboxPopup.titleText = "Coupon ID";
|
||||
lightboxPopup.bodyText = "This alphanumeric text string will be used to ensure " +
|
||||
"that only you can redeem the coupon for the asset that you are sending. Keep it private!";
|
||||
lightboxPopup.button1text = "CLOSE";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
}
|
||||
lightboxPopup.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: couponIDTextField;
|
||||
colorScheme: root.assetCertID === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
|
||||
// Anchors
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.left: couponIDHelp.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
height: 50;
|
||||
// Style
|
||||
activeFocusOnPress: true;
|
||||
activeFocusOnTab: true;
|
||||
|
||||
onAccepted: {
|
||||
optionalMessage.focus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "CHANGE" button
|
||||
HifiControlsUit.Button {
|
||||
id: changeButton;
|
||||
|
@ -939,7 +1073,7 @@ Item {
|
|||
height: 35;
|
||||
width: 100;
|
||||
text: "CHANGE";
|
||||
visible: sendAssetStep.referrer !== "payIn";
|
||||
visible: sendAssetStep.referrer !== "payIn" && sendAssetStep.referrer !== "createCoupon";
|
||||
onClicked: {
|
||||
if (sendAssetStep.referrer === "connections") {
|
||||
root.nextActiveView = "chooseRecipientConnection";
|
||||
|
@ -1263,6 +1397,11 @@ Item {
|
|||
root.assetCertID,
|
||||
parseInt(amountTextField.text),
|
||||
optionalMessage.text);
|
||||
} else if (sendAssetStep.referrer === "createCoupon") {
|
||||
Commerce.authorizeAssetTransfer(couponIDTextField.text || "",
|
||||
root.assetCertID,
|
||||
parseInt(amountTextField.text) || 1,
|
||||
optionalMessage.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1334,18 +1473,24 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125;
|
||||
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 125;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
|
||||
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 50;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
|
||||
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 50;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125;
|
||||
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 125;
|
||||
color: "#FFFFFF";
|
||||
|
||||
RalewaySemiBold {
|
||||
id: paymentSentText;
|
||||
text: root.assetCertID === "" ? "Payment Sent" : (sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent");
|
||||
text: root.assetCertID === "" ? (sendAssetStep.referrer === "createCoupon" ? "Payment Authorized" : "Payment Sent") :
|
||||
(sendAssetStep.referrer === "createCoupon" ? "Item Transfer Authorized" :
|
||||
(sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent"));
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 26;
|
||||
|
@ -1383,6 +1528,8 @@ Item {
|
|||
onClicked: {
|
||||
if (sendAssetStep.referrer === "payIn") {
|
||||
sendToScript({method: "closeSendAsset"});
|
||||
} else if (sendAssetStep.referrer === "createCoupon") {
|
||||
showDidYouCopyLightbox();
|
||||
} else {
|
||||
root.nextActiveView = "sendAssetHome";
|
||||
resetSendAssetData();
|
||||
|
@ -1402,38 +1549,176 @@ Item {
|
|||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
height: 80;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: sendToText_paymentSuccess;
|
||||
text: "Sent To:";
|
||||
// Anchors
|
||||
Item {
|
||||
id: sendToScriptContainer_paymentSuccess;
|
||||
visible: sendAssetStep.referrer === "createCoupon";
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: 90;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RecipientDisplay {
|
||||
anchors.top: parent.top;
|
||||
anchors.left: sendToText_paymentSuccess.right;
|
||||
anchors.right: parent.right;
|
||||
height: parent.height;
|
||||
textColor: hifi.colors.blueAccent;
|
||||
height: childrenRect.height;
|
||||
|
||||
displayName: sendAssetStep.selectedRecipientDisplayName;
|
||||
userName: sendAssetStep.selectedRecipientUserName;
|
||||
profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ?
|
||||
sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : "";
|
||||
multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
|
||||
RalewaySemiBold {
|
||||
id: authorizationIDLabel;
|
||||
text: "Authorization ID:";
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: authorizationIDText.top;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: authorizationIDText;
|
||||
text: root.authorizationID;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: authorizationIDLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: authorizationIDClipboardButton.left;
|
||||
anchors.rightMargin: 16;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
wrapMode: Text.WrapAnywhere;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: authorizationIDClipboardButton;
|
||||
source: "images/clipboard.svg"; // clipboard by Bieutuong Bon from the Noun Project
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
// Anchors
|
||||
anchors.right: parent.right;
|
||||
anchors.top: authorizationIDText.top;
|
||||
height: 40;
|
||||
width: height;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
Window.copyToClipboard(root.authorizationID);
|
||||
authorizationIDText.text = "Copied to Clipboard!\n";
|
||||
authorizationIDClipboardTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: authorizationIDClipboardTimer;
|
||||
interval: 2000;
|
||||
repeat: false;
|
||||
onTriggered: {
|
||||
authorizationIDText.text = root.authorizationID;
|
||||
}
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: couponIDLabel;
|
||||
text: "Coupon ID:";
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: couponIDText.top;
|
||||
width: authorizationIDLabel.width;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: couponIDText;
|
||||
text: root.couponID;
|
||||
anchors.top: authorizationIDText.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: couponIDLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: couponIDClipboardButton.left;
|
||||
anchors.rightMargin: 16;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
wrapMode: Text.WrapAnywhere;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: couponIDClipboardButton;
|
||||
source: "images/clipboard.svg"; // clipboard by Bieutuong Bon from the Noun Project
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
// Anchors
|
||||
anchors.right: parent.right;
|
||||
anchors.top: couponIDText.top;
|
||||
height: 40;
|
||||
width: height;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
Window.copyToClipboard(root.couponID);
|
||||
couponIDText.text = "Copied to Clipboard!\n";
|
||||
couponIDClipboardTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: couponIDClipboardTimer;
|
||||
interval: 2000;
|
||||
repeat: false;
|
||||
onTriggered: {
|
||||
couponIDText.text = root.couponID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
id: sendToRecipientContainer_paymentSuccess;
|
||||
visible: !sendToScriptContainer_paymentSuccess.visible;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 80;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: sendToText_paymentSuccess;
|
||||
text: "Sent To:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: 90;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RecipientDisplay {
|
||||
anchors.top: parent.top;
|
||||
anchors.left: sendToText_paymentSuccess.right;
|
||||
anchors.right: parent.right;
|
||||
height: parent.height;
|
||||
textColor: hifi.colors.blueAccent;
|
||||
|
||||
displayName: sendAssetStep.selectedRecipientDisplayName;
|
||||
userName: sendAssetStep.selectedRecipientUserName;
|
||||
profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ?
|
||||
sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : "";
|
||||
multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: giftContainer_paymentSuccess;
|
||||
|
@ -1448,7 +1733,8 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: gift_paymentSuccess;
|
||||
text: sendAssetStep.referrer === "payIn" ? "Item:" : "Gift:";
|
||||
text: sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "createCoupon" ?
|
||||
"Item:" : "Gift:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
@ -1566,6 +1852,8 @@ Item {
|
|||
onClicked: {
|
||||
if (sendAssetStep.referrer === "payIn") {
|
||||
sendToScript({method: "closeSendAsset"});
|
||||
} else if (sendAssetStep.referrer === "createCoupon") {
|
||||
showDidYouCopyLightbox();
|
||||
} else {
|
||||
root.nextActiveView = "sendAssetHome";
|
||||
resetSendAssetData();
|
||||
|
@ -1599,13 +1887,17 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 150;
|
||||
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 150;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
|
||||
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 50;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
|
||||
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 50;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 300;
|
||||
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 300;
|
||||
color: "#FFFFFF";
|
||||
|
||||
RalewaySemiBold {
|
||||
|
@ -1657,8 +1949,9 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: paymentFailureDetailText;
|
||||
text: "The recipient you specified was unable to receive your " +
|
||||
(root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift."));
|
||||
text: sendAssetStep.referrer === "createCoupon" ? "The server was unable to handle your request. Please try again later." :
|
||||
("The recipient you specified was unable to receive your " +
|
||||
(root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift.")));
|
||||
anchors.top: paymentFailureText.bottom;
|
||||
anchors.topMargin: 20;
|
||||
anchors.left: parent.left;
|
||||
|
@ -1676,7 +1969,8 @@ Item {
|
|||
|
||||
Item {
|
||||
id: sendToContainer_paymentFailure;
|
||||
visible: root.assetCertID === "" || sendAssetStep.referrer === "payIn";
|
||||
visible: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") &&
|
||||
sendAssetStep.referrer !== "createCoupon";
|
||||
anchors.top: paymentFailureDetailText.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
|
@ -1718,7 +2012,8 @@ Item {
|
|||
Item {
|
||||
id: amountContainer_paymentFailure;
|
||||
visible: root.assetCertID === "";
|
||||
anchors.top: sendToContainer_paymentFailure.bottom;
|
||||
anchors.top: sendToContainer_paymentFailure.visible ?
|
||||
sendToContainer_paymentFailure.bottom : paymentFailureDetailText.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
|
@ -1839,6 +2134,11 @@ Item {
|
|||
root.assetCertID,
|
||||
parseInt(amountTextField.text),
|
||||
optionalMessage.text);
|
||||
} else if (sendAssetStep.referrer === "createCoupon") {
|
||||
Commerce.authorizeAssetTransfer(couponIDTextField.text || "",
|
||||
root.assetCertID,
|
||||
parseInt(amountTextField.text) || 1,
|
||||
optionalMessage.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1867,6 +2167,39 @@ Item {
|
|||
sendAssetStep.referrer = "";
|
||||
}
|
||||
|
||||
function generateRandomCouponID() {
|
||||
var RANDOM_COUPON_ID_LENGTH = 25;
|
||||
var randomCouponID = "";
|
||||
var possibleCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for (var i = 0; i < RANDOM_COUPON_ID_LENGTH; i++) {
|
||||
randomCouponID += possibleCharacters.charAt(Math.floor(Math.random() * possibleCharacters.length));
|
||||
}
|
||||
|
||||
return randomCouponID;
|
||||
}
|
||||
|
||||
function showDidYouCopyLightbox() {
|
||||
lightboxPopup.titleText = "Close Confirmation";
|
||||
lightboxPopup.bodyText = "Did you copy your Authorization ID and your Coupon ID?\n\n" +
|
||||
"You won't be able to see your Authorization ID or your Coupon ID once " +
|
||||
"you close this window.";
|
||||
lightboxPopup.button1text = "GO BACK";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
}
|
||||
lightboxPopup.button2text = "I'M ALL SET";
|
||||
lightboxPopup.button2method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
root.nextActiveView = "sendAssetHome";
|
||||
resetSendAssetData();
|
||||
if (root.assetName !== "") {
|
||||
sendSignalToParent({method: "closeSendAsset"});
|
||||
}
|
||||
}
|
||||
lightboxPopup.visible = true;
|
||||
}
|
||||
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
|
@ -1908,9 +2241,15 @@ Item {
|
|||
sendAssetStep.referrer = "payIn";
|
||||
sendAssetStep.selectedRecipientNodeID = "";
|
||||
sendAssetStep.selectedRecipientDisplayName = "Determined by script:";
|
||||
sendAssetStep.selectedRecipientUserName = message.username;
|
||||
sendAssetStep.selectedRecipientUserName = message.username || "";
|
||||
optionalMessage.text = message.message || "No Message Provided";
|
||||
|
||||
if (sendAssetStep.selectedRecipientUserName === "") {
|
||||
console.log("SendAsset: Script didn't specify a recipient username!");
|
||||
sendAssetHome.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
root.nextActiveView = "sendAssetStep";
|
||||
break;
|
||||
case 'inspectionCertificate_resetCert':
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 44 55" enable-background="new 0 0 44 44" xml:space="preserve"><path d="M30.201,10.811h-1.599v-0.076c0-0.754-0.612-1.367-1.366-1.367h-2.063c0.005-0.074,0.023-0.146,0.023-0.225 c0-1.766-1.432-3.197-3.197-3.197s-3.197,1.432-3.197,3.197c0,0.078,0.018,0.15,0.023,0.225h-2.063 c-0.754,0-1.366,0.613-1.366,1.367v0.076h-1.599c-1.38,0-2.502,1.123-2.502,2.502v22.24c0,1.379,1.122,2.502,2.502,2.502h16.402 c1.38,0,2.502-1.123,2.502-2.502v-22.24C32.703,11.934,31.581,10.811,30.201,10.811z M22,7.893c0.691,0,1.251,0.559,1.251,1.25 s-0.56,1.252-1.251,1.252s-1.251-0.561-1.251-1.252S21.309,7.893,22,7.893z M31.035,35.553c0,0.459-0.374,0.834-0.834,0.834H13.799 c-0.46,0-0.834-0.375-0.834-0.834v-22.24c0-0.459,0.374-0.834,0.834-0.834h1.599v1.443h13.205v-1.443h1.599 c0.46,0,0.834,0.375,0.834,0.834V35.553z"/><rect x="15.397" y="16.648" width="13.205" height="0.975"/><rect x="15.397" y="20.402" width="13.205" height="0.973"/><rect x="15.397" y="24.154" width="13.205" height="0.975"/><rect x="15.397" y="27.908" width="13.205" height="0.973"/><rect x="15.397" y="31.66" width="13.205" height="0.975"/><text x="0" y="59" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Bieutuong Bon</text><text x="0" y="64" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,10 @@
|
|||
<svg width="55" height="67" viewBox="0 0 55 67" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="39" cy="21" r="2" fill="#1398BB"/>
|
||||
<circle cx="45" cy="21" r="2" fill="#1398BB"/>
|
||||
<path d="M45 32H10V65L14.5 60.5L18.5 65L23 60.5L27.5 65L32 60.5L36 65L40.5 60.5L45 65V32Z" stroke="#1398BB" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M45 2V16H10V2H45Z" stroke="#1398BB" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M45 42H49C51.2091 42 53 40.2091 53 38V20C53 17.7909 51.2091 16 49 16H6C3.79086 16 2 17.7909 2 20V38C2 40.2091 3.79086 42 6 42H10" stroke="#1398BB" stroke-width="3"/>
|
||||
<path d="M21 47L35 33" stroke="#1398BB" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="34" cy="46" r="3.5" stroke="#1398BB" stroke-width="3"/>
|
||||
<path d="M18.1273 32C17.7319 32.5669 17.5 33.2564 17.5 34C17.5 35.933 19.067 37.5 21 37.5C22.933 37.5 24.5 35.933 24.5 34C24.5 33.2564 24.2681 32.5669 23.8727 32" stroke="#1398BB" stroke-width="3"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1 KiB |
|
@ -270,9 +270,11 @@ Item {
|
|||
model: transactionHistoryModel;
|
||||
delegate: Item {
|
||||
width: parent.width;
|
||||
height: (model.transaction_type === "pendingCount" && model.count !== 0) ? 40 : ((model.status === "confirmed" || model.status === "invalidated") ? transactionText.height + 30 : 0);
|
||||
height: (model.transaction_type === "pendingCount" && model.count !== 0) ? 40 :
|
||||
(transactionContainer.visible ? transactionText.height + 30 : 0);
|
||||
|
||||
Item {
|
||||
id: pendingCountContainer;
|
||||
visible: model.transaction_type === "pendingCount" && model.count !== 0;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
@ -291,7 +293,9 @@ Item {
|
|||
}
|
||||
|
||||
Item {
|
||||
visible: model.transaction_type !== "pendingCount" && (model.status === "confirmed" || model.status === "invalidated");
|
||||
id: transactionContainer;
|
||||
visible: model.transaction_type !== "pendingCount" &&
|
||||
(model.status === "confirmed" || model.status === "invalidated");
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
width: parent.width;
|
||||
|
|
|
@ -873,7 +873,7 @@ Flickable {
|
|||
|
||||
editable: true
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
model: ["None", "Freeze", "Drop"]
|
||||
model: ["None", "Freeze", "Drop", "DropAfterDelay"]
|
||||
label: ""
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
|
|
|
@ -63,6 +63,7 @@ Handler(balance)
|
|||
Handler(inventory)
|
||||
Handler(transferAssetToNode)
|
||||
Handler(transferAssetToUsername)
|
||||
Handler(authorizeAssetTransfer)
|
||||
Handler(alreadyOwned)
|
||||
Handler(availableUpdates)
|
||||
Handler(updateItem)
|
||||
|
@ -203,6 +204,7 @@ QString transactionString(const QJsonObject& valueObject) {
|
|||
int sentMoney = valueObject["sent_money"].toInt();
|
||||
int receivedMoney = valueObject["received_money"].toInt();
|
||||
int dateInteger = valueObject["created_at"].toInt();
|
||||
QString transactionType = valueObject["transaction_type"].toString();
|
||||
QString message = valueObject["message"].toString();
|
||||
QDateTime createdAt(QDateTime::fromSecsSinceEpoch(dateInteger, Qt::UTC));
|
||||
QString result;
|
||||
|
@ -210,8 +212,12 @@ QString transactionString(const QJsonObject& valueObject) {
|
|||
if (sentCerts <= 0 && receivedCerts <= 0 && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) {
|
||||
// this is an hfc transfer.
|
||||
if (sentMoney > 0) {
|
||||
QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString());
|
||||
result += QString("Money sent to %1").arg(recipient);
|
||||
if (transactionType == "escrow") {
|
||||
result += QString("Money transferred to coupon");
|
||||
} else {
|
||||
QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString());
|
||||
result += QString("Money sent to %1").arg(recipient);
|
||||
}
|
||||
} else {
|
||||
QString sender = userLink(valueObject["sender_name"].toString(), valueObject["place_name"].toString());
|
||||
result += QString("Money from %1").arg(sender);
|
||||
|
@ -226,8 +232,12 @@ QString transactionString(const QJsonObject& valueObject) {
|
|||
) {
|
||||
// this is a non-HFC asset transfer.
|
||||
if (sentCerts > 0) {
|
||||
QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString());
|
||||
result += QString("Gift sent to %1").arg(recipient);
|
||||
if (transactionType == "escrow") {
|
||||
result += QString("Item transferred to coupon");
|
||||
} else {
|
||||
QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString());
|
||||
result += QString("Gift sent to %1").arg(recipient);
|
||||
}
|
||||
} else {
|
||||
QString sender = userLink(valueObject["sender_name"].toString(), valueObject["place_name"].toString());
|
||||
result += QString("Gift from %1").arg(sender);
|
||||
|
@ -428,6 +438,7 @@ void Ledger::transferAssetToUsername(const QString& hfc_key, const QString& user
|
|||
transaction["username"] = username;
|
||||
transaction["quantity"] = amount;
|
||||
transaction["message"] = optionalMessage;
|
||||
transaction["place_name"] = DependencyManager::get<AddressManager>()->getPlaceName();
|
||||
if (!certificateID.isEmpty()) {
|
||||
transaction["certificate_id"] = certificateID;
|
||||
}
|
||||
|
@ -440,6 +451,20 @@ void Ledger::transferAssetToUsername(const QString& hfc_key, const QString& user
|
|||
}
|
||||
}
|
||||
|
||||
void Ledger::authorizeAssetTransfer(const QString& hfc_key, const QString& couponID, const QString& certificateID, const int& amount, const QString& optionalMessage) {
|
||||
QJsonObject transaction;
|
||||
transaction["public_key"] = hfc_key;
|
||||
transaction["coupon_id"] = couponID;
|
||||
transaction["quantity"] = amount;
|
||||
transaction["message"] = optionalMessage;
|
||||
if (!certificateID.isEmpty()) {
|
||||
transaction["certificate_id"] = certificateID;
|
||||
}
|
||||
QJsonDocument transactionDoc{ transaction };
|
||||
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||
signedSend("transaction", transactionString, hfc_key, "authorize", "authorizeAssetTransferSuccess", "authorizeAssetTransferFailure");
|
||||
}
|
||||
|
||||
void Ledger::alreadyOwned(const QString& marketplaceId) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
QString endpoint = "already_owned";
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
void certificateInfo(const QString& certificateId);
|
||||
void transferAssetToNode(const QString& hfc_key, const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
||||
void transferAssetToUsername(const QString& hfc_key, const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
||||
void authorizeAssetTransfer(const QString& hfc_key, const QString& couponID, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
||||
void alreadyOwned(const QString& marketplaceId);
|
||||
void getAvailableUpdates(const QString& itemId = "", const int& pageNumber = 1, const int& itemsPerPage = 10);
|
||||
void updateItem(const QString& hfc_key, const QString& certificate_id);
|
||||
|
@ -59,6 +60,7 @@ signals:
|
|||
void certificateInfoResult(QJsonObject result);
|
||||
void transferAssetToNodeResult(QJsonObject result);
|
||||
void transferAssetToUsernameResult(QJsonObject result);
|
||||
void authorizeAssetTransferResult(QJsonObject result);
|
||||
void alreadyOwnedResult(QJsonObject result);
|
||||
void availableUpdatesResult(QJsonObject result);
|
||||
void updateItemResult(QJsonObject result);
|
||||
|
@ -86,6 +88,8 @@ public slots:
|
|||
void transferAssetToNodeFailure(QNetworkReply* reply);
|
||||
void transferAssetToUsernameSuccess(QNetworkReply* reply);
|
||||
void transferAssetToUsernameFailure(QNetworkReply* reply);
|
||||
void authorizeAssetTransferSuccess(QNetworkReply* reply);
|
||||
void authorizeAssetTransferFailure(QNetworkReply* reply);
|
||||
void alreadyOwnedSuccess(QNetworkReply* reply);
|
||||
void alreadyOwnedFailure(QNetworkReply* reply);
|
||||
void availableUpdatesSuccess(QNetworkReply* reply);
|
||||
|
|
|
@ -38,6 +38,7 @@ QmlCommerce::QmlCommerce() {
|
|||
connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus);
|
||||
connect(ledger.data(), &Ledger::transferAssetToNodeResult, this, &QmlCommerce::transferAssetToNodeResult);
|
||||
connect(ledger.data(), &Ledger::transferAssetToUsernameResult, this, &QmlCommerce::transferAssetToUsernameResult);
|
||||
connect(ledger.data(), &Ledger::authorizeAssetTransferResult, this, &QmlCommerce::authorizeAssetTransferResult);
|
||||
connect(ledger.data(), &Ledger::availableUpdatesResult, this, &QmlCommerce::availableUpdatesResult);
|
||||
connect(ledger.data(), &Ledger::updateItemResult, this, &QmlCommerce::updateItemResult);
|
||||
|
||||
|
@ -246,6 +247,21 @@ void QmlCommerce::transferAssetToUsername(const QString& username,
|
|||
ledger->transferAssetToUsername(key, username, certificateID, amount, optionalMessage);
|
||||
}
|
||||
|
||||
void QmlCommerce::authorizeAssetTransfer(const QString& couponID,
|
||||
const QString& certificateID,
|
||||
const int& amount,
|
||||
const QString& optionalMessage) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
QStringList keys = wallet->listPublicKeys();
|
||||
if (keys.count() == 0) {
|
||||
QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } };
|
||||
return emit authorizeAssetTransferResult(result);
|
||||
}
|
||||
QString key = keys[0];
|
||||
ledger->authorizeAssetTransfer(key, couponID, certificateID, amount, optionalMessage);
|
||||
}
|
||||
|
||||
void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) {
|
||||
if (!certificateID.isEmpty()) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
|
|
|
@ -51,6 +51,7 @@ signals:
|
|||
|
||||
void transferAssetToNodeResult(QJsonObject result);
|
||||
void transferAssetToUsernameResult(QJsonObject result);
|
||||
void authorizeAssetTransferResult(QJsonObject result);
|
||||
|
||||
void contentSetChanged(const QString& contentSetHref);
|
||||
|
||||
|
@ -84,6 +85,7 @@ protected:
|
|||
|
||||
Q_INVOKABLE void transferAssetToNode(const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
||||
Q_INVOKABLE void transferAssetToUsername(const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
||||
Q_INVOKABLE void authorizeAssetTransfer(const QString& couponID, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
||||
|
||||
Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID);
|
||||
|
||||
|
|
|
@ -1776,16 +1776,11 @@ int AvatarData::getFauxJointIndex(const QString& name) const {
|
|||
|
||||
int AvatarData::getJointIndex(const QString& name) const {
|
||||
int result = getFauxJointIndex(name);
|
||||
if (result != -1) {
|
||||
return result;
|
||||
}
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
return _fstJointIndices.value(name) - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList AvatarData::getJointNames() const {
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
return _fstJointNames;
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
glm::quat AvatarData::getOrientationOutbound() const {
|
||||
|
@ -2000,8 +1995,6 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
|
||||
_skeletonModelURL = expanded;
|
||||
|
||||
updateJointMappings();
|
||||
|
||||
if (_clientTraitsHandler) {
|
||||
_clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonModelURL);
|
||||
}
|
||||
|
@ -2097,58 +2090,6 @@ void AvatarData::detachAll(const QString& modelURL, const QString& jointName) {
|
|||
setAttachmentData(attachmentData);
|
||||
}
|
||||
|
||||
void AvatarData::setJointMappingsFromNetworkReply() {
|
||||
|
||||
QNetworkReply* networkReply = static_cast<QNetworkReply*>(sender());
|
||||
|
||||
// before we process this update, make sure that the skeleton model URL hasn't changed
|
||||
// since we made the FST request
|
||||
if (networkReply->error() != QNetworkReply::NoError || networkReply->url() != _skeletonModelURL) {
|
||||
qCDebug(avatars) << "Refusing to set joint mappings for FST URL that does not match the current URL";
|
||||
networkReply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
QByteArray line;
|
||||
while (!(line = networkReply->readLine()).isEmpty()) {
|
||||
line = line.trimmed();
|
||||
if (line.startsWith("filename")) {
|
||||
int filenameIndex = line.indexOf('=') + 1;
|
||||
if (filenameIndex > 0) {
|
||||
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
|
||||
}
|
||||
}
|
||||
if (!line.startsWith("jointIndex")) {
|
||||
continue;
|
||||
}
|
||||
int jointNameIndex = line.indexOf('=') + 1;
|
||||
if (jointNameIndex == 0) {
|
||||
continue;
|
||||
}
|
||||
int secondSeparatorIndex = line.indexOf('=', jointNameIndex);
|
||||
if (secondSeparatorIndex == -1) {
|
||||
continue;
|
||||
}
|
||||
QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed();
|
||||
bool ok;
|
||||
int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok);
|
||||
if (ok) {
|
||||
while (_fstJointNames.size() < jointIndex + 1) {
|
||||
_fstJointNames.append(QString());
|
||||
}
|
||||
_fstJointNames[jointIndex] = jointName;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _fstJointNames.size(); i++) {
|
||||
_fstJointIndices.insert(_fstJointNames.at(i), i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
networkReply->deleteLater();
|
||||
}
|
||||
|
||||
void AvatarData::sendAvatarDataPacket(bool sendAll) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -2210,34 +2151,6 @@ void AvatarData::sendIdentityPacket() {
|
|||
_identityDataChanged = false;
|
||||
}
|
||||
|
||||
void AvatarData::updateJointMappings() {
|
||||
{
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
_fstJointIndices.clear();
|
||||
_fstJointNames.clear();
|
||||
_jointData.clear();
|
||||
}
|
||||
|
||||
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
|
||||
////
|
||||
// TODO: Should we rely upon HTTPResourceRequest for ResourceRequestObserver instead?
|
||||
// HTTPResourceRequest::doSend() covers all of the following and
|
||||
// then some. It doesn't cover the connect() call, so we may
|
||||
// want to add a HTTPResourceRequest::doSend() method that does
|
||||
// connects.
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
DependencyManager::get<ResourceRequestObserver>()->update(
|
||||
_skeletonModelURL, -1, "AvatarData::updateJointMappings");
|
||||
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
|
||||
//
|
||||
////
|
||||
connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply);
|
||||
}
|
||||
}
|
||||
|
||||
static const QString JSON_ATTACHMENT_URL = QStringLiteral("modelUrl");
|
||||
static const QString JSON_ATTACHMENT_JOINT_NAME = QStringLiteral("jointName");
|
||||
static const QString JSON_ATTACHMENT_TRANSFORM = QStringLiteral("transform");
|
||||
|
@ -2833,35 +2746,47 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) {
|
|||
qCDebug(avatars) << "discard suspect AvatarEntityData with size =" << avatarEntityData.size();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<QUuid> deletedEntityIDs;
|
||||
QList<QUuid> updatedEntityIDs;
|
||||
|
||||
_avatarEntitiesLock.withWriteLock([&] {
|
||||
if (_avatarEntityData != avatarEntityData) {
|
||||
|
||||
// keep track of entities that were attached to this avatar but no longer are
|
||||
AvatarEntityIDs previousAvatarEntityIDs = QSet<QUuid>::fromList(_avatarEntityData.keys());
|
||||
|
||||
_avatarEntityData = avatarEntityData;
|
||||
setAvatarEntityDataChanged(true);
|
||||
|
||||
deletedEntityIDs.reserve(previousAvatarEntityIDs.size());
|
||||
|
||||
foreach (auto entityID, previousAvatarEntityIDs) {
|
||||
if (!_avatarEntityData.contains(entityID)) {
|
||||
_avatarEntityDetached.insert(entityID);
|
||||
|
||||
if (_clientTraitsHandler) {
|
||||
// we have a client traits handler, so we flag this removed entity as deleted
|
||||
// so that changes are sent next frame
|
||||
_clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID);
|
||||
}
|
||||
deletedEntityIDs.push_back(entityID);
|
||||
}
|
||||
}
|
||||
|
||||
if (_clientTraitsHandler) {
|
||||
// if we have a client traits handler, flag any updated or created entities
|
||||
// so that we send changes for them next frame
|
||||
foreach (auto entityID, _avatarEntityData.keys()) {
|
||||
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID);
|
||||
}
|
||||
}
|
||||
updatedEntityIDs = _avatarEntityData.keys();
|
||||
}
|
||||
});
|
||||
|
||||
if (_clientTraitsHandler) {
|
||||
// we have a client traits handler
|
||||
|
||||
// flag removed entities as deleted so that changes are sent next frame
|
||||
for (auto& deletedEntityID : deletedEntityIDs) {
|
||||
_clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, deletedEntityID);
|
||||
}
|
||||
|
||||
// flag any updated or created entities so that we send changes for them next frame
|
||||
for (auto& entityID : updatedEntityIDs) {
|
||||
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() {
|
||||
|
|
|
@ -1269,11 +1269,6 @@ public slots:
|
|||
*/
|
||||
void sendIdentityPacket();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setJointMappingsFromNetworkReply
|
||||
*/
|
||||
void setJointMappingsFromNetworkReply();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setSessionUUID
|
||||
* @param {Uuid} sessionUUID
|
||||
|
@ -1376,23 +1371,16 @@ protected:
|
|||
mutable HeadData* _headData { nullptr };
|
||||
|
||||
QUrl _skeletonModelURL;
|
||||
QUrl _skeletonFBXURL;
|
||||
QVector<AttachmentData> _attachmentData;
|
||||
QVector<AttachmentData> _oldAttachmentData;
|
||||
QString _displayName;
|
||||
QString _sessionDisplayName { };
|
||||
bool _lookAtSnappingEnabled { true };
|
||||
|
||||
QHash<QString, int> _fstJointIndices; ///< 1-based, since zero is returned for missing keys
|
||||
QStringList _fstJointNames; ///< in order of depth-first traversal
|
||||
|
||||
quint64 _errorLogExpiry; ///< time in future when to log an error
|
||||
|
||||
QWeakPointer<Node> _owningAvatarMixer;
|
||||
|
||||
/// Loads the joint indices, names from the FST file (if any)
|
||||
virtual void updateJointMappings();
|
||||
|
||||
glm::vec3 _targetVelocity;
|
||||
|
||||
SimpleMovingAverage _averageBytesReceived;
|
||||
|
@ -1496,11 +1484,8 @@ protected:
|
|||
T readLockWithNamedJointIndex(const QString& name, const T& defaultValue, F f) const {
|
||||
int index = getFauxJointIndex(name);
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
if (index == -1) {
|
||||
index = _fstJointIndices.value(name) - 1;
|
||||
}
|
||||
|
||||
// The first conditional is superfluous, but illsutrative
|
||||
// The first conditional is superfluous, but illustrative
|
||||
if (index == -1 || index < _jointData.size()) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
@ -1517,9 +1502,6 @@ protected:
|
|||
void writeLockWithNamedJointIndex(const QString& name, F f) {
|
||||
int index = getFauxJointIndex(name);
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
if (index == -1) {
|
||||
index = _fstJointIndices.value(name) - 1;
|
||||
}
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ void ClientTraitsHandler::resetForNewMixer() {
|
|||
}
|
||||
|
||||
void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
||||
Lock lock(_traitLock);
|
||||
std::unique_lock<Mutex> lock(_traitLock);
|
||||
|
||||
if (hasChangedTraits() || _shouldPerformInitialSend) {
|
||||
// we have at least one changed trait to send
|
||||
|
@ -90,13 +90,21 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
|||
_traitStatuses.reset();
|
||||
_hasChangedTraits = false;
|
||||
|
||||
// if this was an initial send of all traits, consider it completed
|
||||
bool initialSend = _shouldPerformInitialSend;
|
||||
_shouldPerformInitialSend = false;
|
||||
|
||||
// we can release the lock here since we've taken a copy of statuses
|
||||
// and will setup the packet using the information in the copy
|
||||
lock.unlock();
|
||||
|
||||
auto simpleIt = traitStatusesCopy.simpleCBegin();
|
||||
while (simpleIt != traitStatusesCopy.simpleCEnd()) {
|
||||
// because the vector contains all trait types (for access using trait type as index)
|
||||
// we double check that it is a simple iterator here
|
||||
auto traitType = static_cast<AvatarTraits::TraitType>(std::distance(traitStatusesCopy.simpleCBegin(), simpleIt));
|
||||
|
||||
if (_shouldPerformInitialSend || *simpleIt == Updated) {
|
||||
if (initialSend || *simpleIt == Updated) {
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
_owningAvatar->packTrait(traitType, *traitsPacketList);
|
||||
|
||||
|
@ -111,12 +119,12 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
|||
auto instancedIt = traitStatusesCopy.instancedCBegin();
|
||||
while (instancedIt != traitStatusesCopy.instancedCEnd()) {
|
||||
for (auto& instanceIDValuePair : instancedIt->instances) {
|
||||
if ((_shouldPerformInitialSend && instanceIDValuePair.value != Deleted)
|
||||
if ((initialSend && instanceIDValuePair.value != Deleted)
|
||||
|| instanceIDValuePair.value == Updated) {
|
||||
// this is a changed trait we need to send or we haven't send out trait information yet
|
||||
// ask the owning avatar to pack it
|
||||
_owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList);
|
||||
} else if (!_shouldPerformInitialSend && instanceIDValuePair.value == Deleted) {
|
||||
} else if (!initialSend && instanceIDValuePair.value == Deleted) {
|
||||
// pack delete for this trait instance
|
||||
AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id,
|
||||
*traitsPacketList);
|
||||
|
@ -127,9 +135,6 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
|||
}
|
||||
|
||||
nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer);
|
||||
|
||||
// if this was an initial send of all traits, consider it completed
|
||||
_shouldPerformInitialSend = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2061,68 +2061,6 @@ bool ScriptEngine::hasEntityScriptDetails(const EntityItemID& entityID) const {
|
|||
return _entityScripts.contains(entityID);
|
||||
}
|
||||
|
||||
const static EntityItemID BAD_SCRIPT_UUID_PLACEHOLDER { "{20170224-dead-face-0000-cee000021114}" };
|
||||
|
||||
void ScriptEngine::processDeferredEntityLoads(const QString& entityScript, const EntityItemID& leaderID) {
|
||||
QList<DeferredLoadEntity> retryLoads;
|
||||
QMutableListIterator<DeferredLoadEntity> i(_deferredEntityLoads);
|
||||
while (i.hasNext()) {
|
||||
auto retry = i.next();
|
||||
if (retry.entityScript == entityScript) {
|
||||
retryLoads << retry;
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
foreach(DeferredLoadEntity retry, retryLoads) {
|
||||
// check whether entity was since been deleted
|
||||
|
||||
EntityScriptDetails details;
|
||||
if (!getEntityScriptDetails(retry.entityID, details)) {
|
||||
qCDebug(scriptengine) << "processDeferredEntityLoads -- entity details gone (entity deleted?)"
|
||||
<< retry.entityID;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check whether entity has since been unloaded or otherwise errored-out
|
||||
if (details.status != EntityScriptStatus::PENDING) {
|
||||
qCDebug(scriptengine) << "processDeferredEntityLoads -- entity status no longer PENDING; "
|
||||
<< retry.entityID << details.status;
|
||||
continue;
|
||||
}
|
||||
|
||||
// propagate leader's failure reasons to the pending entity
|
||||
EntityScriptDetails leaderDetails;
|
||||
{
|
||||
QWriteLocker locker { &_entityScriptsLock };
|
||||
leaderDetails = _entityScripts[leaderID];
|
||||
}
|
||||
if (leaderDetails.status != EntityScriptStatus::RUNNING) {
|
||||
qCDebug(scriptengine) << QString("... pending load of %1 cancelled (leader: %2 status: %3)")
|
||||
.arg(retry.entityID.toString()).arg(leaderID.toString()).arg(leaderDetails.status);
|
||||
|
||||
auto extraDetail = QString("\n(propagated from %1)").arg(leaderID.toString());
|
||||
if (leaderDetails.status == EntityScriptStatus::ERROR_LOADING_SCRIPT ||
|
||||
leaderDetails.status == EntityScriptStatus::ERROR_RUNNING_SCRIPT) {
|
||||
// propagate same error so User doesn't have to hunt down stampede's leader
|
||||
updateEntityScriptStatus(retry.entityID, leaderDetails.status, leaderDetails.errorInfo + extraDetail);
|
||||
} else {
|
||||
// the leader Entity somehow ended up in some other state (rapid-fire delete or unload could cause)
|
||||
updateEntityScriptStatus(retry.entityID, EntityScriptStatus::ERROR_LOADING_SCRIPT,
|
||||
"A previous Entity failed to load using this script URL; reload to try again." + extraDetail);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_occupiedScriptURLs.contains(retry.entityScript)) {
|
||||
qCWarning(scriptengine) << "--- SHOULD NOT HAPPEN -- recursive call into processDeferredEntityLoads" << retry.entityScript;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we made it here then the leading entity was successful so proceed with normal load
|
||||
loadEntityScript(retry.entityID, retry.entityScript, false);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadEntityScript",
|
||||
|
@ -2147,40 +2085,6 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString&
|
|||
updateEntityScriptStatus(entityID, EntityScriptStatus::PENDING, "...pending...");
|
||||
}
|
||||
|
||||
// This "occupied" approach allows multiple Entities to boot from the same script URL while still taking
|
||||
// full advantage of cacheable require modules. This only affects Entities literally coming in back-to-back
|
||||
// before the first one has time to finish loading.
|
||||
if (_occupiedScriptURLs.contains(entityScript)) {
|
||||
auto currentEntityID = _occupiedScriptURLs[entityScript];
|
||||
if (currentEntityID == BAD_SCRIPT_UUID_PLACEHOLDER) {
|
||||
if (forceRedownload) {
|
||||
// script was previously marked unusable, but we're reloading so reset it
|
||||
_occupiedScriptURLs.remove(entityScript);
|
||||
} else {
|
||||
// since not reloading, assume that the exact same input would produce the exact same output again
|
||||
// note: this state gets reset with "reload all scripts," leaving/returning to a Domain, clear cache, etc.
|
||||
#ifdef DEBUG_ENTITY_STATES
|
||||
qCDebug(scriptengine) << QString("loadEntityScript.cancelled entity: %1 (previous script failure)")
|
||||
.arg(entityID.toString());
|
||||
#endif
|
||||
updateEntityScriptStatus(entityID, EntityScriptStatus::ERROR_LOADING_SCRIPT,
|
||||
"A previous Entity failed to load using this script URL; reload to try again.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// another entity is busy loading from this script URL so wait for them to finish
|
||||
#ifdef DEBUG_ENTITY_STATES
|
||||
qCDebug(scriptengine) << QString("loadEntityScript.deferring[%0] entity: %1 (waiting on %2 )")
|
||||
.arg(_deferredEntityLoads.size()).arg(entityID.toString()).arg(currentEntityID.toString());
|
||||
#endif
|
||||
_deferredEntityLoads.push_back({ entityID, entityScript });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// the scriptURL slot is available; flag as in-use
|
||||
_occupiedScriptURLs[entityScript] = entityID;
|
||||
|
||||
#ifdef DEBUG_ENTITY_STATES
|
||||
{
|
||||
EntityScriptDetails details;
|
||||
|
@ -2223,10 +2127,6 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString&
|
|||
qCDebug(scriptengine) << "loadEntityScript.contentAvailable -- aborting";
|
||||
#endif
|
||||
}
|
||||
// recheck whether us since may have been set to BAD_SCRIPT_UUID_PLACEHOLDER in entityScriptContentAvailable
|
||||
if (_occupiedScriptURLs.contains(entityScript) && _occupiedScriptURLs[entityScript] == entityID) {
|
||||
_occupiedScriptURLs.remove(entityScript);
|
||||
}
|
||||
});
|
||||
}, forceRedownload);
|
||||
}
|
||||
|
@ -2301,13 +2201,6 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
newDetails.errorInfo = errorInfo;
|
||||
newDetails.status = status;
|
||||
setEntityScriptDetails(entityID, newDetails);
|
||||
|
||||
#ifdef DEBUG_ENTITY_STATES
|
||||
qCDebug(scriptengine) << "entityScriptContentAvailable -- flagging as BAD_SCRIPT_UUID_PLACEHOLDER";
|
||||
#endif
|
||||
// flag the original entityScript as unusuable
|
||||
_occupiedScriptURLs[entityScript] = BAD_SCRIPT_UUID_PLACEHOLDER;
|
||||
processDeferredEntityLoads(entityScript, entityID);
|
||||
};
|
||||
|
||||
// NETWORK / FILESYSTEM ERRORS
|
||||
|
@ -2441,9 +2334,6 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
callEntityScriptMethod(entityID, "preload");
|
||||
|
||||
emit entityScriptPreloadFinished(entityID);
|
||||
|
||||
_occupiedScriptURLs.remove(entityScript);
|
||||
processDeferredEntityLoads(entityScript, entityID);
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
|
@ -2499,10 +2389,6 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR
|
|||
}
|
||||
|
||||
stopAllTimersForEntityScript(entityID);
|
||||
{
|
||||
// FIXME: shouldn't have to do this here, but currently something seems to be firing unloads moments after firing initial load requests
|
||||
processDeferredEntityLoads(scriptText, entityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2532,7 +2418,6 @@ void ScriptEngine::unloadAllEntityScripts() {
|
|||
_entityScripts.clear();
|
||||
}
|
||||
emit entityScriptDetailsUpdated();
|
||||
_occupiedScriptURLs.clear();
|
||||
|
||||
#ifdef DEBUG_ENGINE_STATE
|
||||
_debugDump(
|
||||
|
|
|
@ -747,7 +747,6 @@ protected:
|
|||
void updateEntityScriptStatus(const EntityItemID& entityID, const EntityScriptStatus& status, const QString& errorInfo = QString());
|
||||
void setEntityScriptDetails(const EntityItemID& entityID, const EntityScriptDetails& details);
|
||||
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
|
||||
void processDeferredEntityLoads(const QString& entityScript, const EntityItemID& leaderID);
|
||||
|
||||
QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
|
||||
void stopTimer(QTimer* timer);
|
||||
|
@ -783,8 +782,6 @@ protected:
|
|||
QSet<QUrl> _includedURLs;
|
||||
mutable QReadWriteLock _entityScriptsLock { QReadWriteLock::Recursive };
|
||||
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
||||
QHash<QString, EntityItemID> _occupiedScriptURLs;
|
||||
QList<DeferredLoadEntity> _deferredEntityLoads;
|
||||
EntityScriptContentAvailableMap _contentAvailableQueue;
|
||||
|
||||
bool _isThreaded { false };
|
||||
|
|
|
@ -319,6 +319,7 @@ public:
|
|||
glBindVertexArray(0);
|
||||
glDeleteVertexArrays(1, &_vao);
|
||||
_canvas->doneCurrent();
|
||||
_canvas->moveToThread(_plugin.thread());
|
||||
}
|
||||
|
||||
void update(const CompositeInfo& newCompositeInfo) { _queue.push(newCompositeInfo); }
|
||||
|
@ -485,6 +486,7 @@ bool OpenVrDisplayPlugin::internalActivate() {
|
|||
_submitCanvas->doneCurrent();
|
||||
});
|
||||
}
|
||||
_submitCanvas->moveToThread(_submitThread.get());
|
||||
}
|
||||
|
||||
return Parent::internalActivate();
|
||||
|
|
|
@ -138,6 +138,8 @@ static QString outOfRangeDataStrategyToString(ViveControllerManager::OutOfRangeD
|
|||
return "Freeze";
|
||||
case ViveControllerManager::OutOfRangeDataStrategy::Drop:
|
||||
return "Drop";
|
||||
case ViveControllerManager::OutOfRangeDataStrategy::DropAfterDelay:
|
||||
return "DropAfterDelay";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +148,8 @@ static ViveControllerManager::OutOfRangeDataStrategy stringToOutOfRangeDataStrat
|
|||
return ViveControllerManager::OutOfRangeDataStrategy::Drop;
|
||||
} else if (string == "Freeze") {
|
||||
return ViveControllerManager::OutOfRangeDataStrategy::Freeze;
|
||||
} else if (string == "DropAfterDelay") {
|
||||
return ViveControllerManager::OutOfRangeDataStrategy::DropAfterDelay;
|
||||
} else {
|
||||
return ViveControllerManager::OutOfRangeDataStrategy::None;
|
||||
}
|
||||
|
@ -302,7 +306,7 @@ void ViveControllerManager::loadSettings() {
|
|||
if (_inputDevice) {
|
||||
const double DEFAULT_ARM_CIRCUMFERENCE = 0.33;
|
||||
const double DEFAULT_SHOULDER_WIDTH = 0.48;
|
||||
const QString DEFAULT_OUT_OF_RANGE_STRATEGY = "Drop";
|
||||
const QString DEFAULT_OUT_OF_RANGE_STRATEGY = "DropAfterDelay";
|
||||
_inputDevice->_armCircumference = settings.value("armCircumference", QVariant(DEFAULT_ARM_CIRCUMFERENCE)).toDouble();
|
||||
_inputDevice->_shoulderWidth = settings.value("shoulderWidth", QVariant(DEFAULT_SHOULDER_WIDTH)).toDouble();
|
||||
_inputDevice->_outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(settings.value("outOfRangeDataStrategy", QVariant(DEFAULT_OUT_OF_RANGE_STRATEGY)).toString());
|
||||
|
@ -516,6 +520,7 @@ void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceInde
|
|||
_nextSimPoseData.vrPoses[deviceIndex].bPoseIsValid &&
|
||||
poseIndex <= controller::TRACKED_OBJECT_15) {
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
controller::Pose pose;
|
||||
switch (_outOfRangeDataStrategy) {
|
||||
case OutOfRangeDataStrategy::Drop:
|
||||
|
@ -544,6 +549,22 @@ void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceInde
|
|||
_nextSimPoseData.angularVelocities[deviceIndex] = _lastSimPoseData.angularVelocities[deviceIndex];
|
||||
}
|
||||
break;
|
||||
case OutOfRangeDataStrategy::DropAfterDelay:
|
||||
const uint64_t DROP_DELAY_TIME = 500 * USECS_PER_MSEC;
|
||||
|
||||
// All Running_OK results are valid.
|
||||
if (_nextSimPoseData.vrPoses[deviceIndex].eTrackingResult == vr::TrackingResult_Running_OK) {
|
||||
pose = buildPose(_nextSimPoseData.poses[deviceIndex], _nextSimPoseData.linearVelocities[deviceIndex], _nextSimPoseData.angularVelocities[deviceIndex]);
|
||||
// update the timer
|
||||
_simDataRunningOkTimestampMap[deviceIndex] = now;
|
||||
} else if (now - _simDataRunningOkTimestampMap[deviceIndex] < DROP_DELAY_TIME) {
|
||||
// report the pose, even though pose is out-of-range
|
||||
pose = buildPose(_nextSimPoseData.poses[deviceIndex], _nextSimPoseData.linearVelocities[deviceIndex], _nextSimPoseData.angularVelocities[deviceIndex]);
|
||||
} else {
|
||||
// this pose has been out-of-range for too long.
|
||||
pose.valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (pose.valid) {
|
||||
|
|
|
@ -63,7 +63,8 @@ public:
|
|||
enum class OutOfRangeDataStrategy {
|
||||
None,
|
||||
Freeze,
|
||||
Drop
|
||||
Drop,
|
||||
DropAfterDelay
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -205,6 +206,8 @@ private:
|
|||
|
||||
bool _hmdTrackingEnabled { true };
|
||||
|
||||
std::map<uint32_t, uint64_t> _simDataRunningOkTimestampMap;
|
||||
|
||||
QString configToString(Config config);
|
||||
friend class ViveControllerManager;
|
||||
};
|
||||
|
|
|
@ -193,22 +193,52 @@
|
|||
"emitterShouldTrail": {
|
||||
"tooltip": "If enabled, then particles are \"left behind\" as the emitter moves, otherwise they are not."
|
||||
},
|
||||
"particleRadiusTriple": {
|
||||
"tooltip": "The size of each particle.",
|
||||
"jsPropertyName": "particleRadius"
|
||||
},
|
||||
"particleRadius": {
|
||||
"tooltip": "The size of each particle."
|
||||
},
|
||||
"radiusStart": {
|
||||
"tooltip": "The start size of each particle."
|
||||
},
|
||||
"radiusFinish": {
|
||||
"tooltip": "The finish size of each particle."
|
||||
},
|
||||
"radiusSpread": {
|
||||
"tooltip": "The spread in size that each particle is given, resulting in a variety of sizes."
|
||||
},
|
||||
"particleColorTriple": {
|
||||
"tooltip": "The color of each particle.",
|
||||
"jsPropertyName": "color"
|
||||
},
|
||||
"particleColor": {
|
||||
"tooltip": "The color of each particle.",
|
||||
"jsPropertyName": "color"
|
||||
},
|
||||
"colorStart": {
|
||||
"tooltip": "The start color of each particle."
|
||||
},
|
||||
"colorFinish": {
|
||||
"tooltip": "The finish color of each particle."
|
||||
},
|
||||
"colorSpread": {
|
||||
"tooltip": "The spread in color that each particle is given, resulting in a variety of colors."
|
||||
},
|
||||
"particleAlphaTriple": {
|
||||
"tooltip": "The alpha of each particle.",
|
||||
"jsPropertyName": "alpha"
|
||||
},
|
||||
"alpha": {
|
||||
"tooltip": "The alpha of each particle."
|
||||
},
|
||||
"alphaStart": {
|
||||
"tooltip": "The start alpha of each particle."
|
||||
},
|
||||
"alphaFinish": {
|
||||
"tooltip": "The finish alpha of each particle."
|
||||
},
|
||||
"alphaSpread": {
|
||||
"tooltip": "The spread in alpha that each particle is given, resulting in a variety of alphas."
|
||||
},
|
||||
|
@ -218,20 +248,44 @@
|
|||
"accelerationSpread": {
|
||||
"tooltip": "The spread in accelerations that each particle is given, resulting in a variety of accelerations."
|
||||
},
|
||||
"particleSpinTriple": {
|
||||
"tooltip": "The spin of each particle.",
|
||||
"jsPropertyName": "particleSpin"
|
||||
},
|
||||
"particleSpin": {
|
||||
"tooltip": "The spin of each particle in the system."
|
||||
"tooltip": "The spin of each particle."
|
||||
},
|
||||
"spinStart": {
|
||||
"tooltip": "The start spin of each particle."
|
||||
},
|
||||
"spinFinish": {
|
||||
"tooltip": "The finish spin of each particle."
|
||||
},
|
||||
"spinSpread": {
|
||||
"tooltip": "The spread in spin that each particle is given, resulting in a variety of spins."
|
||||
},
|
||||
"rotateWithEntity": {
|
||||
"tooltip": "If enabled, each particle will spin relative to the roation of the entity as a whole."
|
||||
"tooltip": "If enabled, each particle will spin relative to the rotation of the entity as a whole."
|
||||
},
|
||||
"particlePolarTriple": {
|
||||
"tooltip": "The angle range in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis.",
|
||||
"skipJSProperty": true
|
||||
},
|
||||
"polarStart": {
|
||||
"tooltip": "The angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis."
|
||||
"tooltip": "The start angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis."
|
||||
},
|
||||
"polarFinish": {
|
||||
"tooltip": "The finish angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis."
|
||||
},
|
||||
"particleAzimuthTriple": {
|
||||
"tooltip": "The angle range in deg at which particles are emitted. Starts in the entity's -x direction, and rotates around its z axis.",
|
||||
"skipJSProperty": true
|
||||
},
|
||||
"azimuthStart": {
|
||||
"tooltip": "The angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis."
|
||||
"tooltip": "The start angle in deg at which particles are emitted. Starts in the entity's -x direction, and rotates around its z axis."
|
||||
},
|
||||
"azimuthFinish": {
|
||||
"tooltip": "The finish angle in deg at which particles are emitted. Starts in the entity's -x direction, and rotates around its z axis."
|
||||
},
|
||||
"lightColor": {
|
||||
"tooltip": "The color of the light emitted.",
|
||||
|
@ -267,7 +321,7 @@
|
|||
"jsPropertyName": "parentMaterialName"
|
||||
},
|
||||
"selectSubmesh": {
|
||||
"tooltip": "If enabled, \"Select Submesh\" property will show up, otherwise \"Material Name to Replace\" will be shown.",
|
||||
"tooltip": "If enabled, \"Submesh to Replace\" property will show up, otherwise \"Material to Replace\" will be shown.",
|
||||
"skipJSProperty": true
|
||||
},
|
||||
"priority": {
|
||||
|
@ -352,7 +406,7 @@
|
|||
"tooltip": "The URL of a sound to play when the entity collides with something else."
|
||||
},
|
||||
"grab.grabbable": {
|
||||
"tooltip": "If enabled, this entity will allow grabbing input and will be moveable."
|
||||
"tooltip": "If enabled, this entity will allow grabbing input and will be movable."
|
||||
},
|
||||
"grab.triggerable": {
|
||||
"tooltip": "If enabled, the collider on this entity is used for triggering events."
|
||||
|
|
|
@ -598,13 +598,19 @@ div.section[collapsed="true"], div.section[collapsed="true"] > .section-header {
|
|||
|
||||
.triple-label {
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
padding: 6px 0;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.triple-item {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.triple-item.rgb.fstuple {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.section-header[collapsed="true"] {
|
||||
margin-bottom: -21px;
|
||||
}
|
||||
|
@ -911,14 +917,17 @@ div.refresh input[type="button"] {
|
|||
clear: both;
|
||||
}
|
||||
|
||||
.draggable-number-container {
|
||||
flex: 0 1 124px;
|
||||
}
|
||||
.draggable-number {
|
||||
position: relative;
|
||||
}
|
||||
.draggable-number div {
|
||||
height: 28px;
|
||||
width: 124px;
|
||||
flex: 0 1 124px;
|
||||
}
|
||||
.draggable-number.text {
|
||||
|
||||
.draggable-number .text {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
color: #afafaf;
|
||||
background-color: #252525;
|
||||
|
@ -930,11 +939,12 @@ div.refresh input[type="button"] {
|
|||
width: 100%;
|
||||
line-height: 2;
|
||||
box-sizing: border-box;
|
||||
z-index: 1;
|
||||
}
|
||||
.draggable-number.text:hover {
|
||||
.draggable-number .text:hover {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.draggable-number span {
|
||||
.draggable-number .left-arrow, .draggable-number .right-arrow {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
font-family: HiFi-Glyphs;
|
||||
|
@ -944,12 +954,12 @@ div.refresh input[type="button"] {
|
|||
.draggable-number span:hover {
|
||||
cursor: default;
|
||||
}
|
||||
.draggable-number.left-arrow {
|
||||
.draggable-number .left-arrow {
|
||||
top: 3px;
|
||||
left: 0px;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.draggable-number.right-arrow {
|
||||
.draggable-number .right-arrow {
|
||||
top: 3px;
|
||||
right: 0px;
|
||||
}
|
||||
|
@ -1381,6 +1391,10 @@ input[type=button]#export {
|
|||
cursor: col-resize;
|
||||
}
|
||||
|
||||
#entity-table .dragging {
|
||||
background-color: #b3ecff;
|
||||
}
|
||||
|
||||
#entity-table td {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
@ -1508,12 +1522,14 @@ input.rename-entity {
|
|||
}
|
||||
|
||||
.create-app-tooltip {
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
background: #6a6a6a;
|
||||
border: 1px solid black;
|
||||
width: 258px;
|
||||
min-height: 20px;
|
||||
padding: 5px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.create-app-tooltip .create-app-tooltip-description {
|
||||
|
@ -1629,10 +1645,6 @@ input.number-slider {
|
|||
flex-flow: column;
|
||||
}
|
||||
|
||||
.flex-column + .flex-column {
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
@ -1645,6 +1657,7 @@ input.number-slider {
|
|||
font-family: Raleway-Light;
|
||||
font-size: 14px;
|
||||
margin: 6px 0;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#property-name, #property-id {
|
||||
|
@ -1657,9 +1670,38 @@ input.number-slider {
|
|||
}
|
||||
|
||||
#placeholder-property-type {
|
||||
min-width: 0px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.collapse-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#property-userData-editor.error {
|
||||
border: 2px solid red;
|
||||
}
|
||||
|
||||
#property-userData-editorStatus {
|
||||
color: white;
|
||||
background-color: red;
|
||||
padding: 5px;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#property-materialData-editor.error {
|
||||
border: 2px solid red;
|
||||
}
|
||||
|
||||
#property-materialData-editorStatus {
|
||||
color: white;
|
||||
background-color: red;
|
||||
padding: 5px;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=number].hide-spinner::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<script type="text/javascript" src="js/spinButtons.js"></script>
|
||||
<script type="text/javascript" src="js/listView.js"></script>
|
||||
<script type="text/javascript" src="js/entityListContextMenu.js"></script>
|
||||
<script type="text/javascript" src="js/utils.js"></script>
|
||||
<script type="text/javascript" src="js/entityList.js"></script>
|
||||
</head>
|
||||
<body onload='loaded();' id="entity-list-body">
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<script type="text/javascript" src="js/underscore-min.js"></script>
|
||||
<script type="text/javascript" src="js/createAppTooltip.js"></script>
|
||||
<script type="text/javascript" src="js/draggableNumber.js"></script>
|
||||
<script type="text/javascript" src="js/utils.js"></script>
|
||||
<script type="text/javascript" src="js/entityProperties.js"></script>
|
||||
<script src="js/jsoneditor.min.js"></script>
|
||||
</head>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||
<script type="text/javascript" src="js/spinButtons.js"></script>
|
||||
<script type="text/javascript" src="js/utils.js"></script>
|
||||
<script type="text/javascript" src="js/gridControls.js"></script>
|
||||
</head>
|
||||
<body onload='loaded();'>
|
||||
|
|
|
@ -170,8 +170,8 @@ DraggableNumber.prototype = {
|
|||
this.elDiv = document.createElement('div');
|
||||
this.elDiv.className = "draggable-number";
|
||||
|
||||
this.elText = document.createElement('label');
|
||||
this.elText.className = "draggable-number text";
|
||||
this.elText = document.createElement('span');
|
||||
this.elText.className = "text";
|
||||
this.elText.innerText = " ";
|
||||
this.elText.style.visibility = "visible";
|
||||
this.elText.addEventListener("mousedown", this.onMouseDown);
|
||||
|
@ -179,15 +179,15 @@ DraggableNumber.prototype = {
|
|||
|
||||
this.elLeftArrow = document.createElement('span');
|
||||
this.elRightArrow = document.createElement('span');
|
||||
this.elLeftArrow.className = 'draggable-number left-arrow';
|
||||
this.elLeftArrow.className = 'left-arrow';
|
||||
this.elLeftArrow.innerHTML = 'D';
|
||||
this.elLeftArrow.addEventListener("click", this.onStepDown);
|
||||
this.elRightArrow.className = 'draggable-number right-arrow';
|
||||
this.elRightArrow.className = 'right-arrow';
|
||||
this.elRightArrow.innerHTML = 'D';
|
||||
this.elRightArrow.addEventListener("click", this.onStepUp);
|
||||
|
||||
this.elInput = document.createElement('input');
|
||||
this.elInput.className = "draggable-number input";
|
||||
this.elInput.className = "input";
|
||||
this.elInput.setAttribute("type", "number");
|
||||
if (this.min !== undefined) {
|
||||
this.elInput.setAttribute("min", this.min);
|
||||
|
@ -205,8 +205,8 @@ DraggableNumber.prototype = {
|
|||
this.elInput.addEventListener("focus", this.showInput.bind(this));
|
||||
|
||||
this.elDiv.appendChild(this.elLeftArrow);
|
||||
this.elDiv.appendChild(this.elText);
|
||||
this.elDiv.appendChild(this.elInput);
|
||||
this.elDiv.appendChild(this.elRightArrow);
|
||||
this.elDiv.appendChild(this.elText);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -20,6 +20,9 @@ const MAX_LENGTH_RADIUS = 9;
|
|||
const MINIMUM_COLUMN_WIDTH = 24;
|
||||
const SCROLLBAR_WIDTH = 20;
|
||||
const RESIZER_WIDTH = 10;
|
||||
const DELTA_X_MOVE_COLUMNS_THRESHOLD = 2;
|
||||
const DELTA_X_COLUMN_SWAP_POSITION = 5;
|
||||
const CERTIFIED_PLACEHOLDER = "** Certified **";
|
||||
|
||||
const COLUMNS = {
|
||||
type: {
|
||||
|
@ -107,8 +110,8 @@ const COLUMNS = {
|
|||
};
|
||||
|
||||
const COMPARE_ASCENDING = function(a, b) {
|
||||
let va = a[currentSortColumn];
|
||||
let vb = b[currentSortColumn];
|
||||
let va = a[currentSortColumnID];
|
||||
let vb = b[currentSortColumnID];
|
||||
|
||||
if (va < vb) {
|
||||
return -1;
|
||||
|
@ -171,7 +174,7 @@ let entityList = null; // The ListView
|
|||
*/
|
||||
let entityListContextMenu = null;
|
||||
|
||||
let currentSortColumn = 'type';
|
||||
let currentSortColumnID = 'type';
|
||||
let currentSortOrder = ASCENDING_SORT;
|
||||
let elSortOrders = {};
|
||||
let typeFilters = [];
|
||||
|
@ -179,10 +182,13 @@ let isFilterInView = false;
|
|||
|
||||
let columns = [];
|
||||
let columnsByID = {};
|
||||
let currentResizeEl = null;
|
||||
let startResizeEvent = null;
|
||||
let lastResizeEvent = null;
|
||||
let resizeColumnIndex = 0;
|
||||
let startThClick = null;
|
||||
let elTargetTh = null;
|
||||
let elTargetSpan = null;
|
||||
let targetColumnIndex = 0;
|
||||
let lastColumnSwapPosition = -1;
|
||||
let initialThEvent = null;
|
||||
let renameTimeout = null;
|
||||
let renameLastBlur = null;
|
||||
let renameLastEntityID = null;
|
||||
|
@ -229,10 +235,6 @@ const PROFILE = !ENABLE_PROFILING ? PROFILE_NOOP : function(name, fn, args) {
|
|||
console.log("PROFILE-Web " + profileIndent + "(" + name + ") End " + delta + "ms");
|
||||
};
|
||||
|
||||
debugPrint = function (message) {
|
||||
console.log(message);
|
||||
};
|
||||
|
||||
function loaded() {
|
||||
openEventBridge(function() {
|
||||
elEntityTable = document.getElementById("entity-table");
|
||||
|
@ -323,10 +325,11 @@ function loaded() {
|
|||
for (let columnID in COLUMNS) {
|
||||
let columnData = COLUMNS[columnID];
|
||||
|
||||
let thID = "entity-" + columnID;
|
||||
let elTh = document.createElement("th");
|
||||
let thID = "entity-" + columnID;
|
||||
elTh.setAttribute("id", thID);
|
||||
elTh.setAttribute("data-resizable-column-id", thID);
|
||||
elTh.setAttribute("columnIndex", columnIndex);
|
||||
elTh.setAttribute("columnID", columnID);
|
||||
if (columnData.glyph) {
|
||||
let elGlyph = document.createElement("span");
|
||||
elGlyph.className = "glyph";
|
||||
|
@ -335,20 +338,20 @@ function loaded() {
|
|||
} else {
|
||||
elTh.innerText = columnData.columnHeader;
|
||||
}
|
||||
elTh.onmousedown = function() {
|
||||
startThClick = this;
|
||||
};
|
||||
elTh.onmouseup = function() {
|
||||
if (startThClick === this) {
|
||||
setSortColumn(columnID);
|
||||
elTh.onmousedown = function(event) {
|
||||
if (event.target.nodeName === 'TH') {
|
||||
elTargetTh = event.target;
|
||||
targetColumnIndex = parseInt(elTargetTh.getAttribute("columnIndex"));
|
||||
lastColumnSwapPosition = event.clientX;
|
||||
} else if (event.target.nodeName === 'SPAN') {
|
||||
elTargetSpan = event.target;
|
||||
}
|
||||
startThClick = null;
|
||||
initialThEvent = event;
|
||||
};
|
||||
|
||||
let elResizer = document.createElement("span");
|
||||
elResizer.className = "resizer";
|
||||
elResizer.innerHTML = " ";
|
||||
elResizer.setAttribute("columnIndex", columnIndex);
|
||||
elResizer.onmousedown = onStartResize;
|
||||
elTh.appendChild(elResizer);
|
||||
|
||||
|
@ -629,10 +632,11 @@ function loaded() {
|
|||
id: entity.id,
|
||||
name: entity.name,
|
||||
type: type,
|
||||
url: filename,
|
||||
fullUrl: entity.url,
|
||||
url: entity.certificateID === "" ? filename : "<i>" + CERTIFIED_PLACEHOLDER + "</i>",
|
||||
fullUrl: entity.certificateID === "" ? filename : CERTIFIED_PLACEHOLDER,
|
||||
locked: entity.locked,
|
||||
visible: entity.visible,
|
||||
certificateID: entity.certificateID,
|
||||
verticesCount: displayIfNonZero(entity.verticesCount),
|
||||
texturesCount: displayIfNonZero(entity.texturesCount),
|
||||
texturesSize: decimalMegabytes(entity.texturesSize),
|
||||
|
@ -758,13 +762,13 @@ function loaded() {
|
|||
refreshNoEntitiesMessage();
|
||||
}
|
||||
|
||||
function setSortColumn(column) {
|
||||
function setSortColumn(columnID) {
|
||||
PROFILE("set-sort-column", function() {
|
||||
if (currentSortColumn === column) {
|
||||
if (currentSortColumnID === columnID) {
|
||||
currentSortOrder *= -1;
|
||||
} else {
|
||||
elSortOrders[currentSortColumn].innerHTML = "";
|
||||
currentSortColumn = column;
|
||||
elSortOrders[currentSortColumnID].innerHTML = "";
|
||||
currentSortColumnID = columnID;
|
||||
currentSortOrder = ASCENDING_SORT;
|
||||
}
|
||||
refreshSortOrder();
|
||||
|
@ -773,7 +777,7 @@ function loaded() {
|
|||
}
|
||||
|
||||
function refreshSortOrder() {
|
||||
elSortOrders[currentSortColumn].innerHTML = currentSortOrder === ASCENDING_SORT ? ASCENDING_STRING : DESCENDING_STRING;
|
||||
elSortOrders[currentSortColumnID].innerHTML = currentSortOrder === ASCENDING_SORT ? ASCENDING_STRING : DESCENDING_STRING;
|
||||
}
|
||||
|
||||
function refreshEntities() {
|
||||
|
@ -870,7 +874,7 @@ function loaded() {
|
|||
if (column.data.glyph) {
|
||||
elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null;
|
||||
} else {
|
||||
elCell.innerText = itemData[column.data.propertyID];
|
||||
elCell.innerHTML = itemData[column.data.propertyID];
|
||||
}
|
||||
elCell.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;";
|
||||
elCell.className = createColumnClassName(column.columnID);
|
||||
|
@ -1089,8 +1093,8 @@ function loaded() {
|
|||
}
|
||||
|
||||
function onStartResize(event) {
|
||||
startResizeEvent = event;
|
||||
resizeColumnIndex = parseInt(this.getAttribute("columnIndex"));
|
||||
lastResizeEvent = event;
|
||||
resizeColumnIndex = parseInt(this.parentNode.getAttribute("columnIndex"));
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
|
@ -1133,8 +1137,37 @@ function loaded() {
|
|||
entityList.refresh();
|
||||
}
|
||||
|
||||
document.onmousemove = function(ev) {
|
||||
if (startResizeEvent) {
|
||||
function swapColumns(columnAIndex, columnBIndex) {
|
||||
let columnA = columns[columnAIndex];
|
||||
let columnB = columns[columnBIndex];
|
||||
let columnATh = columns[columnAIndex].elTh;
|
||||
let columnBTh = columns[columnBIndex].elTh;
|
||||
let columnThParent = columnATh.parentNode;
|
||||
columnThParent.removeChild(columnBTh);
|
||||
columnThParent.insertBefore(columnBTh, columnATh);
|
||||
columnATh.setAttribute("columnIndex", columnBIndex);
|
||||
columnBTh.setAttribute("columnIndex", columnAIndex);
|
||||
columnA.elResizer.setAttribute("columnIndex", columnBIndex);
|
||||
columnB.elResizer.setAttribute("columnIndex", columnAIndex);
|
||||
|
||||
for (let i = 0; i < visibleEntities.length; ++i) {
|
||||
let elRow = visibleEntities[i].elRow;
|
||||
if (elRow) {
|
||||
let columnACell = elRow.childNodes[columnAIndex];
|
||||
let columnBCell = elRow.childNodes[columnBIndex];
|
||||
elRow.removeChild(columnBCell);
|
||||
elRow.insertBefore(columnBCell, columnACell);
|
||||
}
|
||||
}
|
||||
|
||||
columns[columnAIndex] = columnB;
|
||||
columns[columnBIndex] = columnA;
|
||||
|
||||
updateColumnWidths();
|
||||
}
|
||||
|
||||
document.onmousemove = function(event) {
|
||||
if (lastResizeEvent) {
|
||||
startTh = null;
|
||||
|
||||
let column = columns[resizeColumnIndex];
|
||||
|
@ -1146,7 +1179,7 @@ function loaded() {
|
|||
}
|
||||
|
||||
let fullWidth = elEntityTableBody.offsetWidth;
|
||||
let dx = ev.clientX - startResizeEvent.clientX;
|
||||
let dx = event.clientX - lastResizeEvent.clientX;
|
||||
let dPct = dx / fullWidth;
|
||||
|
||||
let newColWidth = column.width + dPct;
|
||||
|
@ -1156,14 +1189,60 @@ function loaded() {
|
|||
column.width += dPct;
|
||||
nextColumn.width -= dPct;
|
||||
updateColumnWidths();
|
||||
startResizeEvent = ev;
|
||||
lastResizeEvent = event;
|
||||
}
|
||||
} else if (elTargetTh) {
|
||||
let dxFromInitial = event.clientX - initialThEvent.clientX;
|
||||
if (Math.abs(dxFromInitial) >= DELTA_X_MOVE_COLUMNS_THRESHOLD) {
|
||||
elTargetTh.className = "dragging";
|
||||
}
|
||||
if (targetColumnIndex < columns.length - 1) {
|
||||
let nextColumnIndex = targetColumnIndex + 1;
|
||||
let nextColumnTh = columns[nextColumnIndex].elTh;
|
||||
let nextColumnStartX = nextColumnTh.getBoundingClientRect().left;
|
||||
if (event.clientX >= nextColumnStartX && event.clientX - lastColumnSwapPosition >= DELTA_X_COLUMN_SWAP_POSITION) {
|
||||
swapColumns(targetColumnIndex, nextColumnIndex);
|
||||
targetColumnIndex = nextColumnIndex;
|
||||
lastColumnSwapPosition = event.clientX;
|
||||
}
|
||||
}
|
||||
if (targetColumnIndex >= 1) {
|
||||
let prevColumnIndex = targetColumnIndex - 1;
|
||||
let prevColumnTh = columns[prevColumnIndex].elTh;
|
||||
let prevColumnEndX = prevColumnTh.getBoundingClientRect().right;
|
||||
if (event.clientX <= prevColumnEndX && lastColumnSwapPosition - event.clientX >= DELTA_X_COLUMN_SWAP_POSITION) {
|
||||
swapColumns(prevColumnIndex, targetColumnIndex);
|
||||
targetColumnIndex = prevColumnIndex;
|
||||
lastColumnSwapPosition = event.clientX;
|
||||
}
|
||||
}
|
||||
} else if (elTargetSpan) {
|
||||
let dxFromInitial = event.clientX - initialThEvent.clientX;
|
||||
if (Math.abs(dxFromInitial) >= DELTA_X_MOVE_COLUMNS_THRESHOLD) {
|
||||
elTargetTh = elTargetSpan.parentNode;
|
||||
elTargetTh.className = "dragging";
|
||||
targetColumnIndex = parseInt(elTargetTh.getAttribute("columnIndex"));
|
||||
lastColumnSwapPosition = event.clientX;
|
||||
elTargetSpan = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.onmouseup = function(ev) {
|
||||
startResizeEvent = null;
|
||||
ev.stopPropagation();
|
||||
document.onmouseup = function(event) {
|
||||
if (elTargetTh) {
|
||||
if (elTargetTh.className !== "dragging" && elTargetTh === event.target) {
|
||||
let columnID = elTargetTh.getAttribute("columnID");
|
||||
setSortColumn(columnID);
|
||||
}
|
||||
elTargetTh.className = "";
|
||||
} else if (elTargetSpan) {
|
||||
let columnID = elTargetSpan.parentNode.getAttribute("columnID");
|
||||
setSortColumn(columnID);
|
||||
}
|
||||
lastResizeEvent = null;
|
||||
elTargetTh = null;
|
||||
elTargetSpan = null;
|
||||
initialThEvent = null;
|
||||
};
|
||||
|
||||
function setSpaceMode(spaceMode) {
|
||||
|
@ -1283,8 +1362,9 @@ function loaded() {
|
|||
});
|
||||
|
||||
augmentSpinButtons();
|
||||
disableDragDrop();
|
||||
|
||||
document.addEventListener("contextmenu", function (event) {
|
||||
document.addEventListener("contextmenu", function(event) {
|
||||
entityListContextMenu.close();
|
||||
|
||||
// Disable default right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -108,6 +108,7 @@ function loaded() {
|
|||
});
|
||||
|
||||
augmentSpinButtons();
|
||||
disableDragDrop();
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
|
||||
});
|
||||
|
|
|
@ -9,10 +9,6 @@
|
|||
const SCROLL_ROWS = 2; // number of rows used as scrolling buffer, each time we pass this number of rows we scroll
|
||||
const FIRST_ROW_INDEX = 2; // the first elRow element's index in the child nodes of the table body
|
||||
|
||||
debugPrint = function (message) {
|
||||
console.log(message);
|
||||
};
|
||||
|
||||
function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction, updateRowFunction, clearRowFunction,
|
||||
preRefreshFunction, postRefreshFunction, preResizeFunction, WINDOW_NONVARIABLE_HEIGHT) {
|
||||
this.elTableBody = elTableBody;
|
||||
|
@ -246,7 +242,7 @@ ListView.prototype = {
|
|||
|
||||
resize: function() {
|
||||
if (!this.elTableBody || !this.elTableScroll) {
|
||||
debugPrint("ListView.resize - no valid table body or table scroll element");
|
||||
console.log("ListView.resize - no valid table body or table scroll element");
|
||||
return;
|
||||
}
|
||||
this.preResizeFunction();
|
||||
|
@ -288,7 +284,7 @@ ListView.prototype = {
|
|||
|
||||
initialize: function() {
|
||||
if (!this.elTableBody || !this.elTableScroll) {
|
||||
debugPrint("ListView.initialize - no valid table body or table scroll element");
|
||||
console.log("ListView.initialize - no valid table body or table scroll element");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
27
scripts/system/html/js/utils.js
Normal file
27
scripts/system/html/js/utils.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// utils.js
|
||||
//
|
||||
// Created by David Back on 19 Nov 2018
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function disableDragDrop() {
|
||||
document.addEventListener("drop", function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener("dragover", function(event) {
|
||||
event.dataTransfer.effectAllowed = "none";
|
||||
event.dataTransfer.dropEffect = "none";
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener("dragenter", function(event) {
|
||||
event.dataTransfer.effectAllowed = "none";
|
||||
event.dataTransfer.dropEffect = "none";
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
}
|
|
@ -164,7 +164,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
var cameraPosition = Camera.position;
|
||||
PROFILE("getMultipleProperties", function () {
|
||||
var multipleProperties = Entities.getMultipleEntityProperties(ids, ['name', 'type', 'locked',
|
||||
'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script']);
|
||||
'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID']);
|
||||
for (var i = 0; i < multipleProperties.length; i++) {
|
||||
var properties = multipleProperties[i];
|
||||
|
||||
|
@ -184,6 +184,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
url: url,
|
||||
locked: properties.locked,
|
||||
visible: properties.visible,
|
||||
certificateID: properties.certificateID,
|
||||
verticesCount: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.verticesCount) : ""),
|
||||
texturesCount: (properties.renderInfo !== undefined ?
|
||||
|
|
Loading…
Reference in a new issue