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

This commit is contained in:
Sam Gateau 2019-05-14 18:22:20 -07:00
commit c163c92a6f
100 changed files with 5900 additions and 212 deletions

View file

@ -53,6 +53,10 @@ Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio.
Change the Solution Configuration (menu ribbon under the menu bar, next to the green play button) from "Debug" to "Release" for best performance.
Create another environment variable (see Step #4)
* Set "Variable name": `PreferredToolArchitecture`
* Set "Variable value": `x64`
Run from the menu bar `Build > Build Solution`.
### Step 7. Testing Interface

View file

@ -17,6 +17,7 @@
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonValue>
#include <shared/QtHelpers.h>
#include <LogHandler.h>
#include <NetworkAccessManager.h>
@ -269,6 +270,13 @@ void AudioMixer::sendStatsPacket() {
return;
}
#ifdef DEBUG_EVENT_QUEUE
QJsonObject qtStats;
_slavePool.queueStats(qtStats);
statsObject["audio_thread_event_queue"] = qtStats;
#endif
// general stats
statsObject["useDynamicJitterBuffers"] = _numStaticJitterFrames == DISABLE_STATIC_JITTER_FRAMES;

View file

@ -113,6 +113,19 @@ void AudioMixerSlavePool::each(std::function<void(AudioMixerSlave& slave)> funct
}
}
#ifdef DEBUG_EVENT_QUEUE
void AudioMixerSlavePool::queueStats(QJsonObject& stats) {
unsigned i = 0;
for (auto& slave : _slaves) {
int queueSize = ::hifi::qt::getEventQueueSize(slave.get());
QString queueName = QString("audio_thread_event_queue_%1").arg(i);
stats[queueName] = queueSize;
i++;
}
}
#endif // DEBUG_EVENT_QUEUE
void AudioMixerSlavePool::setNumThreads(int numThreads) {
// clamp to allowed size
{

View file

@ -17,7 +17,7 @@
#include <vector>
#include <QThread>
#include <shared/QtHelpers.h>
#include <TBBHelpers.h>
#include "AudioMixerSlave.h"
@ -72,6 +72,10 @@ public:
// iterate over all slaves
void each(std::function<void(AudioMixerSlave& slave)> functor);
#ifdef DEBUG_EVENT_QUEUE
void queueStats(QJsonObject& stats);
#endif
void setNumThreads(int numThreads);
int numThreads() { return _numThreads; }

View file

@ -24,6 +24,7 @@
#include <QtCore/QTimer>
#include <QtCore/QThread>
#include <QtCore/QJsonDocument>
#include <shared/QtHelpers.h>
#include <AABox.h>
#include <AvatarLogging.h>
@ -753,6 +754,13 @@ void AvatarMixer::sendStatsPacket() {
statsObject["trailing_mix_ratio"] = _trailingMixRatio;
statsObject["throttling_ratio"] = _throttlingRatio;
#ifdef DEBUG_EVENT_QUEUE
QJsonObject qtStats;
_slavePool.queueStats(qtStats);
statsObject["avatar_thread_event_queue"] = qtStats;
#endif
// this things all occur on the frequency of the tight loop
int tightLoopFrames = _numTightLoopFrames;
int tenTimesPerFrame = tightLoopFrames * 10;

View file

@ -117,6 +117,19 @@ void AvatarMixerSlavePool::each(std::function<void(AvatarMixerSlave& slave)> fun
}
}
#ifdef DEBUG_EVENT_QUEUE
void AvatarMixerSlavePool::queueStats(QJsonObject& stats) {
unsigned i = 0;
for (auto& slave : _slaves) {
int queueSize = ::hifi::qt::getEventQueueSize(slave.get());
QString queueName = QString("avatar_thread_event_queue_%1").arg(i);
stats[queueName] = queueSize;
i++;
}
}
#endif // DEBUG_EVENT_QUEUE
void AvatarMixerSlavePool::setNumThreads(int numThreads) {
// clamp to allowed size
{

View file

@ -20,9 +20,11 @@
#include <TBBHelpers.h>
#include <NodeList.h>
#include <shared/QtHelpers.h>
#include "AvatarMixerSlave.h"
class AvatarMixerSlavePool;
class AvatarMixerSlaveThread : public QThread, public AvatarMixerSlave {
@ -72,6 +74,10 @@ public:
// iterate over all slaves
void each(std::function<void(AvatarMixerSlave& slave)> functor);
#ifdef DEBUG_EVENT_QUEUE
void AvatarMixerSlavePool::queueStats(QJsonObject& stats);
#endif
void setNumThreads(int numThreads);
int numThreads() const { return _numThreads; }

View file

@ -27,6 +27,12 @@
#include "ClientTraitsHandler.h"
#include "AvatarLogging.h"
MixerAvatar::~MixerAvatar() {
if (_challengeTimeout) {
_challengeTimeout->deleteLater();
}
}
void MixerAvatar::fetchAvatarFST() {
_verifyState = nonCertified;
@ -229,6 +235,7 @@ void MixerAvatar::processCertifyEvents() {
QJsonDocument responseJson = QJsonDocument::fromJson(_dynamicMarketResponse.toUtf8());
QString ownerPublicKey;
bool ownerValid = false;
_pendingEvent = false;
if (responseJson["status"].toString() == "success") {
QJsonValue jsonData = responseJson["data"];
if (jsonData.isObject()) {
@ -251,6 +258,7 @@ void MixerAvatar::processCertifyEvents() {
}
sendOwnerChallenge();
_verifyState = challengeClient;
_pendingEvent = true;
} else {
_verifyState = error;
}
@ -259,7 +267,6 @@ void MixerAvatar::processCertifyEvents() {
"message:" << responseJson["message"].toString();
_verifyState = error;
}
_pendingEvent = false;
break;
}
@ -295,6 +302,7 @@ void MixerAvatar::processCertifyEvents() {
}
case requestingOwner:
case challengeClient:
{ // Qt networking done on this thread:
QCoreApplication::processEvents();
break;
@ -324,12 +332,21 @@ void MixerAvatar::sendOwnerChallenge() {
nonceHash.addData(nonce);
_challengeNonceHash = nonceHash.result();
static constexpr int CHALLENGE_TIMEOUT_MS = 10 * 1000; // 10 s
_challengeTimeout.setInterval(CHALLENGE_TIMEOUT_MS);
_challengeTimeout.connect(&_challengeTimeout, &QTimer::timeout, [this]() {
_verifyState = verificationFailed;
_needsIdentityUpdate = true;
static constexpr int CHALLENGE_TIMEOUT_MS = 5 * 1000; // 5 s
if (_challengeTimeout) {
_challengeTimeout->deleteLater();
}
_challengeTimeout = new QTimer();
_challengeTimeout->setInterval(CHALLENGE_TIMEOUT_MS);
_challengeTimeout->setSingleShot(true);
_challengeTimeout->connect(_challengeTimeout, &QTimer::timeout, this, [this]() {
if (_verifyState == challengeClient) {
_pendingEvent = false;
_verifyState = verificationFailed;
_needsIdentityUpdate = true;
}
});
_challengeTimeout->start();
}
void MixerAvatar::handleChallengeResponse(ReceivedMessage* response) {
@ -337,7 +354,6 @@ void MixerAvatar::handleChallengeResponse(ReceivedMessage* response) {
QByteArray encryptedNonce;
QMutexLocker certifyLocker(&_avatarCertifyLock);
if (_verifyState == challengeClient) {
_challengeTimeout.stop();
_challengeResponse = response->readAll();
_verifyState = challengeResponse;
_pendingEvent = true;

View file

@ -21,6 +21,7 @@ class ResourceRequest;
class MixerAvatar : public AvatarData {
public:
~MixerAvatar();
bool getNeedsHeroCheck() const { return _needsHeroCheck; }
void setNeedsHeroCheck(bool needsHeroCheck = true) { _needsHeroCheck = needsHeroCheck; }
@ -53,7 +54,7 @@ private:
QString _ownerPublicKey;
QByteArray _challengeNonceHash;
QByteArray _challengeResponse;
QTimer _challengeTimeout;
QTimer* _challengeTimeout { nullptr };
bool _needsIdentityUpdate { false };
bool generateFSTHash();

View file

@ -372,7 +372,7 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
// Record explicitly filtered-in entity so that extra entities can be flagged.
entityNodeData->insertSentFilteredEntity(entityID);
}
OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData);
OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData, entityNode->getCanGetAndSetPrivateUserData());
if (appendEntityState != OctreeElement::COMPLETED) {
if (appendEntityState == OctreeElement::PARTIAL) {

View file

@ -7,7 +7,8 @@
#
# Construct a default QT location from a root path, a version and an architecture
function(calculate_default_qt_dir _RESULT_NAME)
function(calculate_default_qt_dir _QT_VERSION _RESULT_NAME)
if (ANDROID)
set(QT_DEFAULT_ARCH "android_armv7")
elseif(UWP)
@ -27,22 +28,22 @@ function(calculate_default_qt_dir _RESULT_NAME)
endif()
set_from_env(QT_ROOT QT_ROOT ${QT_DEFAULT_ROOT})
set_from_env(QT_VERSION QT_VERSION "5.10.1")
set_from_env(QT_ARCH QT_ARCH ${QT_DEFAULT_ARCH})
set(${_RESULT_NAME} "${QT_ROOT}/${QT_VERSION}/${QT_ARCH}" PARENT_SCOPE)
set(${_RESULT_NAME} "${QT_ROOT}/${_QT_VERSION}/${QT_ARCH}" PARENT_SCOPE)
endfunction()
# Sets the QT_CMAKE_PREFIX_PATH and QT_DIR variables
# Also enables CMAKE_AUTOMOC and CMAKE_AUTORCC
macro(setup_qt)
set_from_env(QT_VERSION QT_VERSION "5.10.1")
# if QT_CMAKE_PREFIX_PATH was not specified before hand,
# try to use the environment variable
if (NOT QT_CMAKE_PREFIX_PATH)
set(QT_CMAKE_PREFIX_PATH "$ENV{QT_CMAKE_PREFIX_PATH}")
endif()
if (("QT_CMAKE_PREFIX_PATH" STREQUAL "") OR (NOT EXISTS "${QT_CMAKE_PREFIX_PATH}"))
calculate_default_qt_dir(QT_DIR)
calculate_default_qt_dir(${QT_VERSION} QT_DIR)
set(QT_CMAKE_PREFIX_PATH "${QT_DIR}/lib/cmake")
else()
# figure out where the qt dir is

View file

@ -1,5 +1,5 @@
{
"version": 2.2,
"version": 2.3,
"settings": [
{
"name": "metaverse",
@ -224,7 +224,7 @@
"name": "standard_permissions",
"type": "table",
"label": "Domain-Wide User Permissions",
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let&rsquo;s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let&rsquo;s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
"caption": "Standard Permissions",
"can_add_new_rows": false,
"groups": [
@ -233,8 +233,8 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let&rsquo;s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
"span": 10
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let&rsquo;s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
"span": 11
}
],
"columns": [
@ -311,6 +311,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_get_and_set_private_user_data",
"label": "Can Get and Set Private User Data",
"type": "checkbox",
"editable": true,
"default": false
}
],
"non-deletable-row-key": "permissions_id",
@ -337,6 +344,7 @@
"id_can_rez_tmp": true,
"id_can_rez_tmp_certified": true,
"id_can_write_to_asset_server": true,
"id_can_get_and_set_private_user_data": true,
"permissions_id": "localhost"
},
{
@ -361,8 +369,8 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn&rsquo;t have their own row in the per-account section, below.</p>'>?</a>",
"span": 10
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn&rsquo;t have their own row in the per-account section, below.</p>'>?</a>",
"span": 11
}
],
"columns": [
@ -464,6 +472,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_get_and_set_private_user_data",
"label": "Can Get and Set Private User Data",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
@ -482,8 +497,8 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn&rsquo;t have their own row in the per-account section, below.</p>'>?</a>",
"span": 10
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn&rsquo;t have their own row in the per-account section, below.</p>'>?</a>",
"span": 11
}
],
"columns": [
@ -582,6 +597,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_get_and_set_private_user_data",
"label": "Can Get and Set Private User Data",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
@ -596,8 +618,8 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
"span": 10
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
"span": 11
}
],
"columns": [
@ -674,6 +696,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_get_and_set_private_user_data",
"label": "Can Get and Set Private User Data",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
@ -688,8 +717,8 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users from specific IPs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users from specific IPs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 10
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users from specific IPs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users from specific IPs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 11
}
],
"columns": [
@ -766,6 +795,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_get_and_set_private_user_data",
"label": "Can Get and Set Private User Data",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
@ -780,8 +816,8 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific MACs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific MACs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 10
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific MACs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific MACs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 11
}
],
"columns": [
@ -858,7 +894,14 @@
"type": "checkbox",
"editable": true,
"default": false
}
},
{
"name": "id_can_get_and_set_private_user_data",
"label": "Can Get and Set Private User Data",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
{
@ -872,8 +915,8 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 10
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li><li><strong>Can Get and Set Private User Data</strong><br>Sets whether a user can get and set the Private User Data entity property</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 11
}
],
"columns": [
@ -950,6 +993,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_get_and_set_private_user_data",
"label": "Can Get and Set Private User Data",
"type": "checkbox",
"editable": true,
"default": false
}
]
},

View file

@ -282,6 +282,7 @@ void DomainGatekeeper::updateNodePermissions() {
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData;
} else {
// at this point we don't have a sending socket for packets from this node - assume it is the active socket
// or the public socket if we haven't activated a socket for the node yet
@ -374,6 +375,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData;
newNode->setPermissions(userPerms);
return newNode;
}

View file

@ -441,6 +441,12 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena
}
}
if (oldVersion < 2.3) {
unpackPermissions();
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canGetAndSetPrivateUserData);
packPermissions();
}
// write the current description version to our settings
*versionVariant = _descriptionVersion;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View file

@ -16,6 +16,7 @@ import TabletScriptingInterface 1.0
Item {
id: root;
visible: AvatarInputs.showAudioTools || AvatarInputs.showBubbleTools
objectName: "AvatarInputsBar"
property int modality: Qt.NonModal
readonly property bool ignoreRadiusEnabled: AvatarInputs.ignoreRadiusEnabled;
@ -58,6 +59,6 @@ Item {
BubbleIcon {
dragTarget: parent
visible: !root.hmdActive;
visible: !root.hmdActive && AvatarInputs.showBubbleTools;
}
}

View file

@ -147,6 +147,17 @@ Item {
"Parabolas:\t" + root.parabolaPicksUpdated.x + "/" + root.parabolaPicksUpdated.y + "/" + root.parabolaPicksUpdated.z + "\n " +
"Colliders:\t" + root.collisionPicksUpdated.x + "/" + root.collisionPicksUpdated.y + "/" + root.collisionPicksUpdated.z
}
StatText {
visible: { root.eventQueueDebuggingOn && root.expanded }
text: { if (root.eventQueueDebuggingOn) {
return "Event Queue Depth\n " +
"Main:\t" + root.mainThreadQueueDepth + "\n" +
"NodeList:\t" + root.nodeListThreadQueueDepth;
} else {
return "";
}
}
}
}
}

View file

@ -0,0 +1,227 @@
//
// AvatarApp.qml
//
// Created by Zach Fox on 2019-05-02
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import "../simplifiedConstants" as SimplifiedConstants
import "./components" as AvatarAppComponents
import stylesUit 1.0 as HifiStylesUit
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
Rectangle {
id: root
property bool inventoryReceived: false
property bool isDebuggingFirstUseTutorial: false
property bool keyboardRaised: false
property int numUpdatesAvailable: 0
property string avatarPreviewUrl: ""
onAvatarPreviewUrlChanged: {
sendToScript({
"source": "AvatarApp.qml",
"method": "updateAvatarThumbnailURL",
"data": {
"avatarThumbnailURL": root.avatarPreviewUrl
}
});
}
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
color: simplifiedUI.colors.darkBackground
Component.onCompleted: {
Commerce.getLoginStatus();
}
Connections {
target: MyAvatar
onSkeletonModelURLChanged: {
root.updatePreviewUrl();
}
}
Connections {
target: Commerce
onLoginStatusResult: {
if (isLoggedIn) {
Commerce.getWalletStatus();
} else {
// Show some error to the user
}
}
onWalletStatusResult: {
if (walletStatus === 5) {
getInventory();
} else {
// Show some error to the user
}
}
onInventoryResult: {
avatarAppInventoryModel.handlePage(result.status !== "success" && result.message, result);
root.updatePreviewUrl();
}
}
Image {
id: accent
source: "../images/accent.svg"
anchors.top: parent.top
anchors.right: parent.right
width: 60
height: 103
transform: Scale {
yScale: -1
origin.x: accent.width / 2
origin.y: accent.height / 2
}
}
AvatarAppComponents.DisplayNameHeader {
id: displayNameHeader
previewUrl: avatarPreviewUrl
loading: !inventoryContentsList.visible
anchors.top: parent.top
anchors.topMargin: 30
anchors.left: parent.left
anchors.leftMargin: 24
anchors.right: parent.right
anchors.rightMargin: 24
}
Item {
id: avatarInfoTextContainer
width: parent.implicitWidth
height: childrenRect.height
anchors.top: displayNameHeader.bottom
anchors.topMargin: 30
anchors.left: parent.left
anchors.leftMargin: 24
anchors.right: parent.right
anchors.rightMargin: 24
HifiStylesUit.GraphikRegular {
id: yourAvatarsTitle
text: "Your Avatars"
anchors.top: parent.top
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignLeft
color: simplifiedUI.colors.text.white
size: 22
}
HifiStylesUit.GraphikRegular {
id: yourAvatarsSubtitle
text: "These are the avatars that you've created and uploaded via the Avatar Creator."
width: parent.width
wrapMode: Text.WordWrap
anchors.top: yourAvatarsTitle.bottom
anchors.topMargin: 6
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignLeft
color: simplifiedUI.colors.text.darkGrey
size: 14
}
}
HifiModels.PSFListModel {
id: avatarAppInventoryModel
itemsPerPage: 4
listModelName: 'inventory'
listView: inventoryContentsList
getPage: function () {
var editionFilter = "";
var primaryFilter = "avatar";
var titleFilter = "";
Commerce.inventory(
editionFilter,
primaryFilter,
titleFilter,
avatarAppInventoryModel.currentPageToRetrieve,
avatarAppInventoryModel.itemsPerPage
);
}
processPage: function(data) {
inventoryReceived = true;
data.assets.forEach(function (item) {
if (item.status.length > 1) { console.warn("Unrecognized inventory status", item); }
item.status = item.status[0];
});
return data.assets;
}
}
Item {
anchors.top: avatarInfoTextContainer.bottom
anchors.topMargin: 16
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
AnimatedImage {
visible: !inventoryContentsList.visible
anchors.centerIn: parent
width: 72
height: width
source: "../images/loading.gif"
}
ListView {
id: inventoryContentsList
visible: avatarAppInventoryModel.count !== 0
interactive: contentItem.height > height
clip: true
model: avatarAppInventoryModel
anchors.fill: parent
width: parent.width
delegate: AvatarAppComponents.AvatarAppListDelegate {
id: avatarAppListDelegate
itemName: title
itemPreviewImageUrl: preview
itemHref: download_url
standaloneOptimized: model.standalone_optimized
standaloneIncompatible: model.standalone_incompatible
}
}
}
function getInventory() {
avatarAppInventoryModel.getFirstPage();
}
function updatePreviewUrl() {
var previewUrl = "";
var downloadUrl = "";
for (var i = 0; i < avatarAppInventoryModel.count; ++i) {
downloadUrl = avatarAppInventoryModel.get(i).download_url;
previewUrl = avatarAppInventoryModel.get(i).preview;
if (MyAvatar.skeletonModelURL === downloadUrl) {
avatarPreviewUrl = previewUrl;
return;
}
}
}
function fromScript(message) {
switch (message.method) {
default:
console.log('AvatarApp.qml: Unrecognized message from JS');
break;
}
}
signal sendToScript(var message);
}

View file

@ -0,0 +1,124 @@
//
// AvatarAppListDelegate.qml
//
// Created by Zach Fox on 2019-05-09
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import "../../simplifiedConstants" as SimplifiedConstants
import "../../simplifiedControls" as SimplifiedControls
import stylesUit 1.0 as HifiStylesUit
import QtGraphicalEffects 1.0
Rectangle {
id: root
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
property string itemName
property string itemPreviewImageUrl
property string itemHref
property bool standaloneOptimized
property bool standaloneIncompatible
property bool isCurrentItem
property bool isHovering: mouseArea.containsMouse || wearButton.hovered || wearButton.down
height: 102
width: parent.width
color: root.isHovering ? simplifiedUI.colors.darkBackgroundHighlight : "transparent"
Rectangle {
id: borderMask
width: root.isHovering ? itemPreviewImage.width + 4 : itemPreviewImage.width - 4
height: width
radius: width
anchors.centerIn: itemPreviewImage
color: "#FFFFFF"
Behavior on width {
enabled: true
SmoothedAnimation { velocity: 80 }
}
}
Image {
id: itemPreviewImage
source: root.itemPreviewImageUrl
anchors.left: parent.left
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
height: 60
width: height
fillMode: Image.PreserveAspectCrop
mipmap: true
layer.enabled: true
layer.effect: OpacityMask {
maskSource: mask
}
Rectangle {
id: mask
width: itemPreviewImage.width
height: itemPreviewImage.height
radius: itemPreviewImage.width / 2
visible: false
}
}
HifiStylesUit.GraphikRegular {
id: avatarName
text: root.itemName
anchors.left: itemPreviewImage.right
anchors.leftMargin: 20
anchors.right: root.isHovering ? wearButton.left : parent.right
anchors.rightMargin: 20
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight
height: parent.height
size: 20
color: simplifiedUI.colors.text.almostWhite
}
SimplifiedControls.Button {
id: wearButton
visible: MyAvatar.skeletonModelURL !== root.itemHref && root.isHovering
anchors.right: parent.right
anchors.rightMargin: 24
anchors.verticalCenter: parent.verticalCenter
width: 165
height: 32
text: "WEAR"
onClicked: {
MyAvatar.useFullAvatarURL(root.itemHref);
}
}
SimplifiedControls.CheckBox {
id: wornCheckBox
enabled: false
visible: MyAvatar.skeletonModelURL === root.itemHref
anchors.right: parent.right
anchors.rightMargin: 24
anchors.verticalCenter: parent.verticalCenter
width: 14
height: 14
checked: true
}
MouseArea {
z: -1
id: mouseArea
anchors.fill: parent
hoverEnabled: true
}
}

View file

@ -0,0 +1,108 @@
//
// DisplayNameHeader.qml
//
// Created by Wayne Chen on 2019-05-03
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import "../../simplifiedConstants" as SimplifiedConstants
import "../../simplifiedControls" as SimplifiedControls
import stylesUit 1.0 as HifiStylesUit
import controlsUit 1.0 as HifiControlsUit
import QtGraphicalEffects 1.0
Item {
id: root
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
height: itemPreviewImage.height
property string previewUrl: ""
property bool loading: true
AnimatedImage {
visible: root.loading
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
height: 72
width: height
source: "../../images/loading.gif"
}
Image {
id: itemPreviewImage
visible: !root.loading
source: root.previewUrl
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
height: 100
width: height
fillMode: Image.PreserveAspectCrop
layer.enabled: true
layer.effect: OpacityMask {
maskSource: mask
}
mipmap: true
Rectangle {
id: mask
width: itemPreviewImage.width
height: width
radius: width / 2
visible: false
}
}
Item {
id: displayNameContainer
height: itemPreviewImage.height
anchors.right: parent.right
anchors.left: itemPreviewImage.right
anchors.leftMargin: 21
anchors.verticalCenter: parent.verticalCenter
HifiStylesUit.GraphikRegular {
id: displayNameLabel
text: "Display Name"
color: simplifiedUI.colors.text.lightGrey
size: 16
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.verticalCenter
anchors.left: parent.left
verticalAlignment: Text.AlignBottom
}
Item {
id: myDisplayNameContainer
width: parent.width
height: 42
anchors.top: parent.verticalCenter
anchors.right: parent.right
anchors.left: parent.left
SimplifiedControls.TextField {
id: myDisplayNameText
text: MyAvatar.sessionDisplayName === "" ? MyAvatar.displayName : MyAvatar.sessionDisplayName
maximumLength: 256
clip: true
anchors.fill: parent
onEditingFinished: {
if (MyAvatar.displayName !== text) {
MyAvatar.displayName = text;
}
myDisplayNameText.focus = false;
}
onFocusChanged: {
myDisplayNameText.autoScroll = focus;
}
}
}
}
}

View file

@ -0,0 +1,4 @@
<svg width="94" height="175" viewBox="0 0 94 175" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 175L93.4322 175L73.3124 166.208L0 175Z" fill="#009036"/>
<path d="M73.3124 166.208L93.4322 175L93.4322 -1.34999e-05L73.3124 166.208Z" fill="#FF42A7"/>
</svg>

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View file

@ -0,0 +1,192 @@
//
// InputDeviceButton.qml
//
// Created by Zach Fox on 2019-05-02
// Based off of MicBarApplication.qml by Zach Pomerantz and Wayne Chen
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtGraphicalEffects 1.0
import stylesUit 1.0
import TabletScriptingInterface 1.0
import "../simplifiedConstants" as SimplifiedConstants
Rectangle {
id: micBar
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
readonly property var level: AudioScriptingInterface.inputLevel
readonly property var clipping: AudioScriptingInterface.clipping
property var muted: AudioScriptingInterface.muted
property var pushToTalk: AudioScriptingInterface.pushToTalk
property var pushingToTalk: AudioScriptingInterface.pushingToTalk
readonly property var userSpeakingLevel: 0.4
property bool gated: false
readonly property string unmutedIcon: "images/mic-unmute-i.svg"
readonly property string mutedIcon: "images/mic-mute-i.svg"
readonly property string pushToTalkIcon: "images/mic-ptt-i.svg"
readonly property string clippingIcon: "images/mic-clip-i.svg"
readonly property string gatedIcon: "images/mic-gate-i.svg"
Connections {
target: AudioScriptingInterface
onNoiseGateOpened: {
gated = false;
}
onNoiseGateClosed: {
gated = false;
}
}
height: 30
width: 34
opacity: 0.7
onLevelChanged: {
var rectOpacity = (muted && (level >= userSpeakingLevel)) ? 1.0 : 0.7;
if (pushToTalk && !pushingToTalk) {
rectOpacity = (mouseArea.containsMouse) ? 1.0 : 0.7;
} else if (mouseArea.containsMouse && rectOpacity != 1.0) {
rectOpacity = 1.0;
}
micBar.opacity = rectOpacity;
}
color: "#00000000"
MouseArea {
id: mouseArea
anchors {
left: icon.left
right: bar.right
top: icon.top
bottom: icon.bottom
}
hoverEnabled: true
scrollGestureEnabled: false
onClicked: {
if (pushToTalk) {
return;
}
AudioScriptingInterface.muted = !muted;
Tablet.playSound(TabletEnums.ButtonClick);
muted = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
}
onContainsMouseChanged: {
if (containsMouse) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
}
QtObject {
id: colors
readonly property string unmutedColor: simplifiedUI.colors.controls.inputVolumeButton.text.noisy
readonly property string gatedColor: "#00BDFF"
readonly property string mutedColor: simplifiedUI.colors.controls.inputVolumeButton.text.muted
readonly property string gutter: "#575757"
readonly property string greenStart: "#39A38F"
readonly property string greenEnd: "#1FC6A6"
readonly property string yellow: "#C0C000"
readonly property string fill: "#55000000"
readonly property string icon: (muted || clipping) ? mutedColor : gated ? gatedColor : unmutedColor
}
Item {
id: icon
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
width: parent.width - bar.width - bar.anchors.leftMargin
height: parent.height
Item {
anchors.fill: parent
opacity: mouseArea.containsMouse ? 1.0 : 0.7
Image {
id: image
source: (pushToTalk) ? pushToTalkIcon : muted ? mutedIcon :
clipping ? clippingIcon : gated ? gatedIcon : unmutedIcon
anchors.fill: parent
fillMode: Image.PreserveAspectFit
}
ColorOverlay {
id: imageOverlay
anchors { fill: image }
source: image
color: pushToTalk ? (pushingToTalk ? colors.unmutedColor : colors.mutedColor) : colors.icon
}
}
}
Item {
id: bar
anchors {
left: icon.right
leftMargin: 0
verticalCenter: icon.verticalCenter
}
width: 4
height: parent.height
Rectangle { // base
id: baseBar
radius: 4
anchors { fill: parent }
color: colors.gutter
}
Rectangle { // mask
id: mask
height: micBar.muted ? parent.height : parent.height * level
color: micBar.muted ? colors.mutedColor : "white"
width: parent.width
radius: 5
anchors {
bottom: parent.bottom
bottomMargin: 0
left: parent.left
leftMargin: 0
}
}
LinearGradient {
anchors { fill: mask }
visible: mask.visible && !micBar.muted
source: mask
start: Qt.point(0, 0)
end: Qt.point(0, bar.height)
rotation: 180
gradient: Gradient {
GradientStop {
position: 0.0
color: colors.greenStart
}
GradientStop {
position: 0.5
color: colors.greenEnd
}
GradientStop {
position: 1.0
color: colors.yellow
}
}
}
}
}

View file

@ -0,0 +1,10 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.3383 4.13446L23.8713 5.60152C21.9374 3.46761 19.2033 2.06723 16.0691 2.06723C13.0683 2.06723 10.4009 3.40093 8.46707 5.40147L7 3.9344C9.26728 1.53375 12.5348 0 16.0691 0C19.7368 0 23.071 1.60044 25.3383 4.13446ZM21.9376 7.53584L20.4705 9.0029C19.4703 7.66921 17.8698 6.86899 16.0693 6.86899C14.4022 6.86899 12.9351 7.66921 11.8682 8.80285L10.4011 7.33578C11.8015 5.80203 13.802 4.80176 16.0693 4.80176C18.4033 4.80176 20.5372 5.86871 21.9376 7.53584ZM17.9575 30.1572C17.9575 31.1771 17.1307 32.0039 16.1108 32.0039C15.0909 32.0039 14.2642 31.1771 14.2642 30.1572C14.2642 29.1373 15.0909 28.3105 16.1108 28.3105C17.1307 28.3105 17.9575 29.1373 17.9575 30.1572ZM18.3632 11.0801H14.1597L15.0116 25.8539H17.4867L18.3632 11.0801Z" fill="#EA4C5F"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.1999 4.72587C17.5049 4.72587 18.5628 3.66795 18.5628 2.36294C18.5628 1.05792 17.5049 0 16.1999 0C14.8948 0 13.8369 1.05792 13.8369 2.36294C13.8369 3.66795 14.8948 4.72587 16.1999 4.72587ZM18.6667 11.8004V14.7337H13.7334V11.8004C13.7334 10.467 14.8667 9.40039 16.2 9.40039C17.6 9.40039 18.6667 10.467 18.6667 11.8004ZM13.7334 20.1332V17.2666H18.6667V20.1332C18.6667 21.4665 17.5333 22.5332 16.2 22.5332C14.8667 22.5332 13.7334 21.4665 13.7334 20.1332ZM23.6665 20.6V17.0667C23.6665 16.4 23.0665 15.9334 22.4665 15.9334C21.7998 15.9334 21.3332 16.4667 21.3332 17.1333V20.6C21.3332 23.0666 19.0665 25.0666 16.3332 25.0666C13.5999 25.0666 11.3333 23.0666 11.3333 20.6V17.0667C11.3333 16.4 10.8666 15.8667 10.2666 15.8667C9.59999 15.8 9 16.2667 9 16.9333V20.6C9 23.9999 11.6666 26.7999 15.1333 27.3332V29.5998H12.2666C11.6 29.5998 11.0666 30.1332 11.0666 30.7998C11.0666 31.4665 11.6 31.9998 12.2666 31.9998H20.4665C21.1332 31.9998 21.6665 31.4665 21.6665 30.7998C21.6665 30.1332 21.1332 29.5998 20.4665 29.5998H17.5332V27.3332C20.9998 26.7332 23.6665 23.9999 23.6665 20.6Z" fill="#00B4EF" fill-opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.6441 4.13328L24.1775 5.59992C22.2442 3.46662 19.5109 2.06664 16.3776 2.06664C13.3776 2.06664 10.711 3.39995 8.77768 5.39993L7.31104 3.93328C9.57767 1.53331 12.8443 0 16.3776 0C20.0442 0 23.3775 1.59998 25.6441 4.13328ZM20.7777 9.00072L22.2444 7.53408C20.8444 5.86743 18.7111 4.80078 16.3778 4.80078C14.1111 4.80078 12.1112 5.80077 10.7112 7.33408L12.1778 8.80073C13.2445 7.66741 14.7111 6.86742 16.3778 6.86742C18.1777 6.86742 19.7777 7.66741 20.7777 9.00072ZM18.8803 12.0758V11.7496C18.8803 10.4445 17.7763 9.40039 16.4775 9.40039C15.1787 9.40039 14.0747 10.4445 14.0747 11.7496V16.2521L18.8803 12.0758ZM14.543 21.5129L12.6113 23.2103C13.4959 24.2599 14.9141 24.9311 16.4774 24.9311C19.14 24.9311 21.348 22.9735 21.348 20.559V17.1658C21.348 16.5132 21.8026 15.9912 22.452 15.9912C23.0364 15.9912 23.6209 16.448 23.6209 17.1005V20.559C23.6209 23.887 21.0233 26.6277 17.6464 27.1498V29.3684H20.5038C21.1532 29.3684 21.6727 29.8905 21.6727 30.543C21.6727 31.1956 21.1532 31.7176 20.5038 31.7176H12.5161C11.8667 31.7176 11.3471 31.1956 11.3471 30.543C11.3471 29.8905 11.8667 29.3684 12.5161 29.3684H15.3085V27.1498C13.5328 26.915 11.9589 26.0045 10.8771 24.7343L8.9443 26.4327C8.48972 26.8242 7.77537 26.759 7.38573 26.3022L7.25585 26.1717C6.8662 25.7149 6.93114 24.9971 7.38573 24.6056L23.8806 9.98853C24.3352 9.597 25.0495 9.66226 25.4392 10.119L25.5691 10.2495C25.9587 10.7716 25.8938 11.4241 25.5041 11.8809L18.8803 17.7015V20.1017C18.8803 21.4068 17.7763 22.4509 16.4775 22.4509C15.6689 22.4509 14.9744 22.0838 14.543 21.5129ZM10.5679 15.9919C11.1523 15.9919 11.6069 16.5139 11.6069 17.1664V18.4715L9.33398 20.4944V17.0359C9.39892 16.4486 9.91845 15.9266 10.5679 15.9919Z" fill="#EA4C5F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.6441 4.13328L24.1775 5.59992C22.2442 3.46662 19.5109 2.06664 16.3776 2.06664C13.3776 2.06664 10.711 3.39995 8.77768 5.39993L7.31104 3.93328C9.57767 1.53331 12.8443 0 16.3776 0C20.0442 0 23.3775 1.59998 25.6441 4.13328ZM20.7777 9.00072L22.2444 7.53408C20.8444 5.86743 18.7111 4.80078 16.3778 4.80078C14.1111 4.80078 12.1112 5.80077 10.7112 7.33408L12.1778 8.80073C13.2445 7.66741 14.7111 6.86742 16.3778 6.86742C18.1777 6.86742 19.7777 7.66741 20.7777 9.00072ZM18.8803 12.0758V11.7496C18.8803 10.4445 17.7763 9.40039 16.4775 9.40039C15.1787 9.40039 14.0747 10.4445 14.0747 11.7496V16.2521L18.8803 12.0758ZM14.543 21.5129L12.6113 23.2103C13.4959 24.2599 14.9141 24.9311 16.4774 24.9311C19.14 24.9311 21.348 22.9735 21.348 20.559V17.1658C21.348 16.5132 21.8026 15.9912 22.452 15.9912C23.0364 15.9912 23.6209 16.448 23.6209 17.1005V20.559C23.6209 23.887 21.0233 26.6277 17.6464 27.1498V29.3684H20.5038C21.1532 29.3684 21.6727 29.8905 21.6727 30.543C21.6727 31.1956 21.1532 31.7176 20.5038 31.7176H12.5161C11.8667 31.7176 11.3471 31.1956 11.3471 30.543C11.3471 29.8905 11.8667 29.3684 12.5161 29.3684H15.3085V27.1498C13.5328 26.915 11.9589 26.0045 10.8771 24.7343L8.9443 26.4327C8.48972 26.8242 7.77537 26.759 7.38573 26.3022L7.25585 26.1717C6.8662 25.7149 6.93114 24.9971 7.38573 24.6056L23.8806 9.98853C24.3352 9.597 25.0495 9.66226 25.4392 10.119L25.5691 10.2495C25.9587 10.7716 25.8938 11.4241 25.5041 11.8809L18.8803 17.7015V20.1017C18.8803 21.4068 17.7763 22.4509 16.4775 22.4509C15.6689 22.4509 14.9744 22.0838 14.543 21.5129ZM10.5679 15.9919C11.1523 15.9919 11.6069 16.5139 11.6069 17.1664V18.4715L9.33398 20.4944V17.0359C9.39893 16.4486 9.91845 15.9266 10.5679 15.9919Z" fill="#EA4C5F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1 @@
<svg id="Art" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><title>mic-ptt-a</title><path class="cls-1" d="M34.52,10.09l-1.85,1.85a13.19,13.19,0,0,0-9.82-4.46,13.35,13.35,0,0,0-9.58,4.2L11.42,9.84a15.66,15.66,0,0,1,23.1.25Zm-6.13,6.13,1.85-1.85a9.62,9.62,0,0,0-14.53-.25L17.55,16a7.34,7.34,0,0,1,5.3-2.44A6.85,6.85,0,0,1,28.39,16.22ZM23,16a3.58,3.58,0,0,1,1.51.3,3.68,3.68,0,0,1,1.26.88,3.88,3.88,0,0,1,.84,1.3A3.94,3.94,0,0,1,26.9,20v5.07a1.91,1.91,0,0,1,.48-.05A3.93,3.93,0,0,1,30,26.07a3.38,3.38,0,0,1,1.5-.32,3.27,3.27,0,0,1,2.77,1.36,2.75,2.75,0,0,1,.85-.1,3.35,3.35,0,0,1,1.33.25,3.18,3.18,0,0,1,1.12.76,3.23,3.23,0,0,1,.73,1.13,3.32,3.32,0,0,1,.24,1.32v3.31a12.27,12.27,0,0,1-.43,3.41l-1.36,5.65a2.67,2.67,0,0,1-1,1.55A2.89,2.89,0,0,1,34,45H23a4.47,4.47,0,0,1-1.76-.43,3.88,3.88,0,0,1-1.36-1.12L14.1,35.7a3.72,3.72,0,0,1-.8-2.35,3.64,3.64,0,0,1,.28-1.5,3.75,3.75,0,0,1,.84-1.27,3.9,3.9,0,0,1,2.77-1.18,4.5,4.5,0,0,1,2,.54V19.88a4.06,4.06,0,0,1,1.13-2.78,3.74,3.74,0,0,1,1.25-.83A3.85,3.85,0,0,1,23,16Zm0,2a1.89,1.89,0,0,0-.74.12,2,2,0,0,0-1.06,1,1.92,1.92,0,0,0-.15.74V35.21l-2.32-3.06a2,2,0,0,0-.7-.59,1.88,1.88,0,0,0-.9-.2,1.85,1.85,0,0,0-.74.15,2,2,0,0,0-.63.43,2,2,0,0,0-.4.63,1.9,1.9,0,0,0-.13.74,2,2,0,0,0,.38,1.17l5.86,7.79a1.79,1.79,0,0,0,.68.57A1.74,1.74,0,0,0,23,43H34a1.23,1.23,0,0,0,.59-.15.88.88,0,0,0,.24-.23.71.71,0,0,0,.13-.31l1.37-5.6a12,12,0,0,0,.37-3V30.43a1.7,1.7,0,0,0-.43-1.07,1.31,1.31,0,0,0-.47-.37,1.35,1.35,0,0,0-.59-.11,1.46,1.46,0,0,0-.55.11,1.23,1.23,0,0,0-.46.32,1.64,1.64,0,0,0-.43,1.07h-.48v-1a1.52,1.52,0,0,0-.12-.66,1.61,1.61,0,0,0-.37-.56,1.63,1.63,0,0,0-1.22-.54,2,2,0,0,0-1.23.54,1.77,1.77,0,0,0-.36.53,1.57,1.57,0,0,0-.11.64v1h-.49V29a2.22,2.22,0,0,0-.58-1.44,1.71,1.71,0,0,0-.62-.44A1.88,1.88,0,0,0,27.4,27a2,2,0,0,0-.74.13,1.85,1.85,0,0,0-.63.41,2,2,0,0,0-.53,1.36v1.5h-.57V20a2,2,0,0,0-.54-1.44,1.75,1.75,0,0,0-.63-.44A1.73,1.73,0,0,0,23,18Z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Art" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M16.7,13.2c-0.3,0.3-0.7,0.6-1,0.9l1.8,1.9c1.4-1.5,3.3-2.4,5.3-2.4c2.2,0,4.2,0.9,5.5,2.7l1.8-1.8
C26.8,10.3,20.8,9.8,16.7,13.2z M32.7,11.9l1.9-1.9c-0.3-0.3-0.6-0.7-1-1c-6.3-5.9-16.2-5.6-22.1,0.7l1.9,1.8
c2.5-2.6,5.9-4.2,9.6-4.2C26.6,7.5,30.2,9.1,32.7,11.9z M38.3,29.1c-0.2-0.4-0.4-0.8-0.7-1.1c-0.3-0.3-0.7-0.6-1.1-0.8
C36,27.1,35.6,27,35.1,27c-0.3,0-0.6,0-0.8,0.1c-0.6-0.9-1.7-1.4-2.8-1.4c-0.5,0-1,0.1-1.5,0.3c-0.7-0.7-1.6-1-2.6-1.1
c-0.2,0-0.3,0-0.5,0.1V20c0-0.5-0.1-1-0.3-1.5c-0.2-0.5-0.5-0.9-0.8-1.3c-0.4-0.4-0.8-0.7-1.3-0.9C24,16.1,23.5,16,23,16
c-0.5,0-1,0.1-1.4,0.3c-0.5,0.2-0.9,0.5-1.3,0.8c-0.7,0.7-1.1,1.7-1.1,2.8v10.1c-0.6-0.3-1.3-0.5-2-0.5c-1,0-2,0.4-2.8,1.2
c-0.4,0.4-0.6,0.8-0.8,1.3c-0.2,0.5-0.3,1-0.3,1.5c0,0.9,0.3,1.7,0.8,2.4l5.8,7.8c0.4,0.5,0.8,0.9,1.4,1.1c0.6,0.3,1.2,0.4,1.8,0.4
h11c0.6,0,1.2-0.2,1.8-0.6c0.5-0.4,0.9-0.9,1-1.6l1.4-5.6c0.3-1.1,0.4-2.3,0.4-3.4v-3.3C38.6,30,38.5,29.6,38.3,29.1z M36.7,33.7
c0,1-0.1,2-0.4,3L35,42.3c0,0.1-0.1,0.2-0.1,0.3c-0.1,0.1-0.1,0.2-0.2,0.2C34.4,42.9,34.2,43,34,43H23c-0.3,0-0.6,0-0.8-0.2
c-0.3-0.1-0.5-0.3-0.7-0.6l-5.9-7.8c-0.2-0.3-0.4-0.7-0.4-1.2c0-0.3,0-0.5,0.1-0.7c0.1-0.2,0.2-0.4,0.4-0.6c0.2-0.2,0.4-0.3,0.6-0.4
c0.2-0.1,0.5-0.2,0.7-0.2c0.3,0,0.6,0.1,0.9,0.2c0.3,0.1,0.5,0.3,0.7,0.6l2.3,3.1V19.9c0-0.3,0.1-0.5,0.2-0.7c0.2-0.5,0.6-0.8,1.1-1
C22.5,18,22.7,18,23,18c0.3,0,0.5,0,0.8,0.1c0.2,0.1,0.5,0.2,0.6,0.4c0.4,0.4,0.6,0.9,0.5,1.4v10.4h0.6v-1.5c0-0.5,0.2-1,0.5-1.4
c0.2-0.2,0.4-0.3,0.6-0.4c0.2-0.1,0.5-0.1,0.7-0.1c0.3,0,0.5,0,0.8,0.1c0.2,0.1,0.4,0.2,0.6,0.4c0.4,0.4,0.6,0.9,0.6,1.4v1.3h0.5v-1
c0-0.2,0-0.4,0.1-0.6c0.1-0.2,0.2-0.4,0.4-0.5c0.3-0.3,0.8-0.5,1.2-0.5c0.5,0,0.9,0.2,1.2,0.5c0.2,0.2,0.3,0.3,0.4,0.6
c0.1,0.2,0.1,0.4,0.1,0.7v1h0.5c0-0.4,0.2-0.8,0.4-1.1c0.1-0.1,0.3-0.3,0.5-0.3c0.2-0.1,0.4-0.1,0.6-0.1c0.2,0,0.4,0,0.6,0.1
c0.2,0.1,0.3,0.2,0.5,0.4c0.3,0.3,0.4,0.7,0.4,1.1V33.7z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.8664 5.59992L25.3331 4.13328C23.0664 1.59998 19.7332 0 16.0665 0C12.5333 0 9.26664 1.53331 7 3.93328L8.46665 5.39993C10.4 3.39995 13.0666 2.06664 16.0665 2.06664C19.1998 2.06664 21.9331 3.46662 23.8664 5.59992ZM20.4664 8.99975L21.9331 7.5331C20.5331 5.86646 18.3998 4.7998 16.0665 4.7998C13.7999 4.7998 11.7999 5.79979 10.3999 7.3331L11.8665 8.79975C12.9332 7.66643 14.3998 6.86644 16.0665 6.86644C17.8665 6.86644 19.4664 7.66643 20.4664 8.99975ZM18.5334 11.8004V14.7337H13.6001V11.8004C13.6001 10.467 14.7334 9.40039 16.0667 9.40039C17.4667 9.40039 18.5334 10.467 18.5334 11.8004ZM13.6001 17.2666V20.1332C13.6001 21.4665 14.7334 22.5332 16.0667 22.5332C17.4 22.5332 18.5334 21.4665 18.5334 20.1332V17.2666H13.6001ZM23.5332 17.0667V20.6C23.5332 23.9999 20.8665 26.7332 17.3999 27.3332V29.5998H20.3332C20.9999 29.5998 21.5332 30.1332 21.5332 30.7998C21.5332 31.4665 20.9999 31.9998 20.3332 31.9998H12.1333C11.4667 31.9998 10.9333 31.4665 10.9333 30.7998C10.9333 30.1332 11.4667 29.5998 12.1333 29.5998H14.9999V27.3332C11.5333 26.7999 8.8667 23.9999 8.8667 20.6V16.9333C8.8667 16.2667 9.46669 15.8 10.1333 15.8667C10.7333 15.8667 11.2 16.4 11.2 17.0667V20.6C11.2 23.0666 13.4666 25.0666 16.1999 25.0666C18.9332 25.0666 21.1999 23.0666 21.1999 20.6V17.1333C21.1999 16.4667 21.6665 15.9334 22.3332 15.9334C22.9332 15.9334 23.5332 16.4 23.5332 17.0667Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.8664 5.59992L25.3331 4.13328C23.0664 1.59998 19.7332 0 16.0665 0C12.5333 0 9.26664 1.53331 7 3.93328L8.46665 5.39993C10.4 3.39995 13.0666 2.06664 16.0665 2.06664C19.1998 2.06664 21.9331 3.46662 23.8664 5.59992ZM20.4664 8.99975L21.9331 7.5331C20.5331 5.86646 18.3998 4.7998 16.0665 4.7998C13.7999 4.7998 11.7999 5.79979 10.3999 7.3331L11.8665 8.79975C12.9332 7.66643 14.3998 6.86644 16.0665 6.86644C17.8665 6.86644 19.4664 7.66643 20.4664 8.99975ZM18.5334 11.8004V14.7337H13.6001V11.8004C13.6001 10.467 14.7334 9.40039 16.0667 9.40039C17.4667 9.40039 18.5334 10.467 18.5334 11.8004ZM13.6001 17.2666V20.1332C13.6001 21.4665 14.7334 22.5332 16.0667 22.5332C17.4 22.5332 18.5334 21.4665 18.5334 20.1332V17.2666H13.6001ZM23.5332 17.0667V20.6C23.5332 23.9999 20.8665 26.7332 17.3999 27.3332V29.5998H20.3332C20.9999 29.5998 21.5332 30.1332 21.5332 30.7998C21.5332 31.4665 20.9999 31.9998 20.3332 31.9998H12.1333C11.4667 31.9998 10.9333 31.4665 10.9333 30.7998C10.9333 30.1332 11.4667 29.5998 12.1333 29.5998H14.9999V27.3332C11.5333 26.7999 8.8667 23.9999 8.8667 20.6V16.9333C8.8667 16.2667 9.46669 15.8 10.1333 15.8667C10.7333 15.8667 11.2 16.4 11.2 17.0667V20.6C11.2 23.0666 13.4666 25.0666 16.1999 25.0666C18.9332 25.0666 21.1999 23.0666 21.1999 20.6V17.1333C21.1999 16.4667 21.6665 15.9334 22.3332 15.9334C22.9332 15.9334 23.5332 16.4 23.5332 17.0667Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,147 @@
//
// SettingsApp.qml
//
// Created by Zach Fox on 2019-05-02
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import "../simplifiedConstants" as SimplifiedConstants
import stylesUit 1.0 as HifiStylesUit
import "./audio" as AudioSettings
import "./general" as GeneralSettings
import "./vr" as VrSettings
Rectangle {
property string activeTabView: "generalTabView"
id: root
color: simplifiedUI.colors.darkBackground
anchors.fill: parent
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
Rectangle {
id: tabContainer
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 64
color: simplifiedUI.colors.highlightOnDark
ListModel {
id: tabListModel
ListElement {
tabTitle: "General"
tabViewName: "generalTabView"
}
ListElement {
tabTitle: "Audio"
tabViewName: "audioTabView"
}
ListElement {
tabTitle: "VR"
tabViewName: "vrTabView"
}
}
Component {
id: highlightBar
Rectangle {
color: simplifiedUI.colors.darkBackground
}
}
ListView {
id: tabListView
anchors.fill: parent
contentHeight: parent.height
contentWidth: childrenRect.width
orientation: ListView.Horizontal
model: tabListModel
highlight: highlightBar
interactive: contentItem.width > width
delegate: Item {
width: tabTitleText.paintedWidth + 64
height: parent.height
HifiStylesUit.GraphikRegular {
id: tabTitleText
color: simplifiedUI.colors.text.white
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: model.tabTitle
size: 24
}
MouseArea {
anchors.fill: parent
onClicked: {
tabListView.currentIndex = index;
root.activeTabView = model.tabViewName;
}
}
}
}
}
Item {
id: tabViewContainers
anchors.top: tabContainer.bottom
anchors.left: parent.left
anchors.leftMargin: 26
anchors.right: parent.right
anchors.rightMargin: 26
anchors.bottom: parent.bottom
GeneralSettings.General {
id: generalTabViewContainer
visible: activeTabView === "generalTabView"
anchors.fill: parent
onSendNameTagInfo: {
sendToScript(message);
}
}
AudioSettings.Audio {
id: audioTabViewContainer
visible: activeTabView === "audioTabView"
anchors.fill: parent
}
VrSettings.VR {
id: vrTabViewContainer
visible: activeTabView === "vrTabView"
anchors.fill: parent
}
}
Image {
source: "../images/accent.svg"
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 94
height: 175
}
function fromScript(message) {
switch (message.method) {
default:
console.log('SettingsApp.qml: Unrecognized message from JS');
break;
}
}
signal sendToScript(var message);
}

View file

@ -0,0 +1,363 @@
//
// Audio.qml
//
// Created by Zach Fox on 2019-05-06
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3
import "../../simplifiedConstants" as SimplifiedConstants
import "../../simplifiedControls" as SimplifiedControls
import stylesUit 1.0 as HifiStylesUit
import QtQuick.Layouts 1.3
Flickable {
id: root
contentWidth: parent.width
contentHeight: audioColumnLayout.height
topMargin: 16
bottomMargin: 16
clip: true
function changePeakValuesEnabled(enabled) {
if (!enabled) {
AudioScriptingInterface.devices.input.peakValuesEnabled = true;
}
}
onVisibleChanged: {
AudioScriptingInterface.devices.input.peakValuesEnabled = visible;
if (visible) {
root.contentX = 0;
root.contentY = -root.topMargin;
AudioScriptingInterface.devices.input.peakValuesEnabledChanged.connect(changePeakValuesEnabled);
} else {
AudioScriptingInterface.devices.input.peakValuesEnabledChanged.disconnect(changePeakValuesEnabled);
}
}
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
ColumnLayout {
id: audioColumnLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
spacing: simplifiedUI.margins.settings.spacingBetweenSettings
ColumnLayout {
id: volumeControlsContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: volumeControlsTitle
text: "Volume Controls"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
SimplifiedControls.Slider {
id: peopleVolume
anchors.left: parent.left
anchors.right: parent.right
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
height: 30
labelText: "People Volume"
from: -60
to: 10
defaultValue: 0.0
value: AudioScriptingInterface.getAvatarGain()
live: true
onValueChanged: {
if (AudioScriptingInterface.getAvatarGain() != peopleVolume.value) {
AudioScriptingInterface.setAvatarGain(peopleVolume.value);
}
}
}
SimplifiedControls.Slider {
id: environmentVolume
anchors.left: parent.left
anchors.right: parent.right
Layout.topMargin: 2
height: 30
labelText: "Environment Volume"
from: -60
to: 10
defaultValue: 0.0
value: AudioScriptingInterface.getInjectorGain()
live: true
onValueChanged: {
if (AudioScriptingInterface.getInjectorGain() != environmentVolume.value) {
AudioScriptingInterface.setInjectorGain(environmentVolume.value);
}
}
}
SimplifiedControls.Slider {
id: systemSoundVolume
anchors.left: parent.left
anchors.right: parent.right
Layout.topMargin: 2
height: 30
labelText: "System Sound Volume"
from: -60
to: 10
defaultValue: 0.0
value: AudioScriptingInterface.getSystemInjectorGain()
live: true
onValueChanged: {
if (AudioScriptingInterface.getSystemInjectorGain() != systemSoundVolume.value) {
AudioScriptingInterface.setSystemInjectorGain(systemSoundVolume.value);
}
}
}
}
ColumnLayout {
id: micControlsContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: micControlsTitle
text: "Default Mute Controls"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ColumnLayout {
id: micControlsSwitchGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
SimplifiedControls.Switch {
id: muteMicrophoneSwitch
width: parent.width
height: 18
labelTextOn: "Mute Microphone"
checked: AudioScriptingInterface.mutedDesktop
onClicked: {
AudioScriptingInterface.mutedDesktop = !AudioScriptingInterface.mutedDesktop;
}
}
SimplifiedControls.Switch {
id: pushToTalkSwitch
width: parent.width
height: 18
labelTextOn: "Push to Talk - Press and Hold \"T\" to Talk"
checked: AudioScriptingInterface.pushToTalkDesktop
onClicked: {
AudioScriptingInterface.pushToTalkDesktop = !AudioScriptingInterface.pushToTalkDesktop;
}
}
}
}
ColumnLayout {
id: inputDeviceContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: inputDeviceTitle
text: "Which input device?"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ButtonGroup { id: inputDeviceButtonGroup }
ListView {
id: inputDeviceListView
anchors.left: parent.left
anchors.right: parent.right
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
clip: true
model: AudioScriptingInterface.devices.input
delegate: Item {
width: parent.width
height: inputDeviceCheckbox.height
SimplifiedControls.RadioButton {
id: inputDeviceCheckbox
anchors.left: parent.left
width: parent.width - inputLevel.width
checked: selectedDesktop
text: model.devicename
ButtonGroup.group: inputDeviceButtonGroup
onClicked: {
AudioScriptingInterface.setStereoInput(false); // the next selected audio device might not support stereo
AudioScriptingInterface.setInputDevice(model.info, false); // `false` argument for Desktop mode setting
}
}
SimplifiedControls.InputPeak {
id: inputLevel
showMuted: AudioScriptingInterface.mutedDesktop
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
peak: model.peak
visible: AudioScriptingInterface.devices.input.peakValuesAvailable
}
}
}
SimplifiedControls.Button {
property bool audioLoopedBack: AudioScriptingInterface.getLocalEcho()
function startAudioLoopback() {
if (!audioLoopedBack) {
audioLoopedBack = true;
AudioScriptingInterface.setLocalEcho(true);
}
}
function stopAudioLoopback() {
if (audioLoopedBack) {
audioLoopedBack = false;
AudioScriptingInterface.setLocalEcho(false);
}
}
Timer {
id: loopbackTimer
interval: 8000
running: false
repeat: false
onTriggered: {
stopAudioLoopback();
}
}
id: testYourMicButton
enabled: !HMD.active
anchors.left: parent.left
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
width: 160
height: 32
text: audioLoopedBack ? "STOP TESTING" : "TEST YOUR MIC"
onClicked: {
if (audioLoopedBack) {
loopbackTimer.stop();
stopAudioLoopback();
} else {
loopbackTimer.restart();
startAudioLoopback();
}
}
}
}
ColumnLayout {
id: outputDeviceContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: outputDeviceTitle
text: "Which output device?"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ButtonGroup { id: outputDeviceButtonGroup }
ListView {
id: outputDeviceListView
anchors.left: parent.left
anchors.right: parent.right
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
clip: true
model: AudioScriptingInterface.devices.output
delegate: Item {
width: parent.width
height: outputDeviceCheckbox.height
SimplifiedControls.RadioButton {
id: outputDeviceCheckbox
anchors.left: parent.left
width: parent.width
checked: selectedDesktop
text: model.devicename
ButtonGroup.group: outputDeviceButtonGroup
onClicked: {
AudioScriptingInterface.setOutputDevice(model.info, false); // `false` argument for Desktop mode setting
}
}
}
}
SimplifiedControls.Button {
property var sound: null
property var sample: null
property bool isPlaying: false
function createSampleSound() {
sound = ApplicationInterface.getSampleSound();
sample = null;
}
function playSound() {
// FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap
// FIXME: AudioScriptingInterface.playSystemSound should not require position
if (sample === null && !isPlaying) {
sample = AudioScriptingInterface.playSystemSound(sound, MyAvatar.qmlPosition);
isPlaying = true;
sample.finished.connect(reset);
}
}
function stopSound() {
if (sample && isPlaying) {
sample.stop();
}
}
function reset() {
sample.finished.disconnect(reset);
isPlaying = false;
sample = null;
}
Component.onCompleted: createSampleSound();
Component.onDestruction: stopSound();
onVisibleChanged: {
if (!visible) {
stopSound();
}
}
id: testYourSoundButton
enabled: !HMD.active
anchors.left: parent.left
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
width: 160
height: 32
text: isPlaying ? "STOP TESTING" : "TEST YOUR SOUND"
onClicked: {
isPlaying ? stopSound() : playSound();
}
}
}
}
}

View file

@ -0,0 +1,210 @@
//
// General.qml
//
// Created by Zach Fox on 2019-05-06
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import "../../simplifiedConstants" as SimplifiedConstants
import "../../simplifiedControls" as SimplifiedControls
import stylesUit 1.0 as HifiStylesUit
import QtQuick.Layouts 1.3
Flickable {
property string avatarNametagMode: Settings.getValue("simplifiedNametag/avatarNametagMode", "on")
id: root
contentWidth: parent.width
contentHeight: generalColumnLayout.height
topMargin: 16
bottomMargin: 16
clip: true
onAvatarNametagModeChanged: {
sendNameTagInfo({method: 'handleAvatarNametagMode', avatarNametagMode: root.avatarNametagMode, source: "SettingsApp.qml"});
}
onVisibleChanged: {
if (visible) {
root.contentX = 0;
root.contentY = -root.topMargin;
}
}
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
ColumnLayout {
id: generalColumnLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
spacing: simplifiedUI.margins.settings.spacingBetweenSettings
ColumnLayout {
id: avatarNameTagsContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: avatarNameTagsTitle
text: "Avatar Name Tags"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ColumnLayout {
id: avatarNameTagsRadioButtonGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
SimplifiedControls.RadioButton {
id: avatarNameTagsOff
text: "Off"
checked: root.avatarNametagMode === "off"
onClicked: {
root.avatarNametagMode = "off"
}
}
SimplifiedControls.RadioButton {
id: avatarNameTagsAlwaysOn
text: "Always On"
checked: root.avatarNametagMode === "alwaysOn"
onClicked: {
root.avatarNametagMode = "alwaysOn"
}
}
SimplifiedControls.RadioButton {
id: avatarNameTagsClickToView
text: "Click to View"
checked: root.avatarNametagMode === "on"
onClicked: {
root.avatarNametagMode = "on"
}
}
}
}
ColumnLayout {
id: performanceContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: performanceTitle
text: "Performance"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ColumnLayout {
id: performanceRadioButtonGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
SimplifiedControls.RadioButton {
id: performanceLow
text: "Eco"
}
SimplifiedControls.RadioButton {
id: performanceMedium
text: "Interactive"
}
SimplifiedControls.RadioButton {
id: performanceHigh
text: "Realtime"
}
}
}
ColumnLayout {
id: cameraContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: cameraTitle
text: "Camera View"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ColumnLayout {
id: cameraRadioButtonGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
SimplifiedControls.RadioButton {
id: firstPerson
text: "First Person View"
checked: Camera.mode === "first person"
onClicked: {
Camera.mode = "first person"
}
}
SimplifiedControls.RadioButton {
id: thirdPerson
text: "Third Person View"
checked: Camera.mode === "third person"
onClicked: {
Camera.mode = "third person"
}
}
Connections {
target: Camera
onModeUpdated: {
if (Camera.mode === "first person") {
firstPerson.checked = true
} else if (Camera.mode === "third person") {
thirdPerson.checked = true
}
}
}
}
}
HifiStylesUit.GraphikRegular {
id: logoutText
text: (AccountServices.username === "Unknown user" ? "Log In" : "Logout " + AccountServices.username)
wrapMode: Text.Wrap
width: paintedWidth
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.lightBlue
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
parent.color = simplifiedUI.colors.text.lightBlueHover;
}
onExited: {
parent.color = simplifiedUI.colors.text.lightBlue;
}
onClicked: {
if (Account.loggedIn) {
AccountServices.logOut();
} else {
DialogsManager.showLoginDialog();
}
}
}
}
}
signal sendNameTagInfo(var message);
}

View file

@ -0,0 +1,376 @@
//
// VR.qml
//
// Created by Zach Fox on 2019-05-08
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3
import "../../simplifiedConstants" as SimplifiedConstants
import "../../simplifiedControls" as SimplifiedControls
import stylesUit 1.0 as HifiStylesUit
import QtQuick.Layouts 1.3
Flickable {
id: root
contentWidth: parent.width
contentHeight: vrColumnLayout.height
topMargin: 16
bottomMargin: 16
clip: true
function changePeakValuesEnabled(enabled) {
if (!enabled) {
AudioScriptingInterface.devices.input.peakValuesEnabled = true;
}
}
onVisibleChanged: {
AudioScriptingInterface.devices.input.peakValuesEnabled = visible;
if (visible) {
root.contentX = 0;
root.contentY = -root.topMargin;
AudioScriptingInterface.devices.input.peakValuesEnabledChanged.connect(changePeakValuesEnabled);
} else {
AudioScriptingInterface.devices.input.peakValuesEnabledChanged.disconnect(changePeakValuesEnabled);
}
}
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
ColumnLayout {
id: vrColumnLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
spacing: simplifiedUI.margins.settings.spacingBetweenSettings
ColumnLayout {
id: controlsContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: controlsTitle
text: "VR Movement Controls"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ColumnLayout {
id: controlsRadioButtonGroup
width: parent.width
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
ButtonGroup { id: controlsButtonGroup }
SimplifiedControls.RadioButton {
id: controlsDefault
text: "Default"
ButtonGroup.group: controlsButtonGroup
checked: MyAvatar.getControlScheme() === 0
onClicked: {
MyAvatar.setControlScheme(0);
}
}
SimplifiedControls.RadioButton {
id: controlsAnalog
text: "Analog"
ButtonGroup.group: controlsButtonGroup
checked: MyAvatar.getControlScheme() === 1
onClicked: {
MyAvatar.setControlScheme(1);
}
}
Item {
id: controlsAdvancedContainer
Layout.minimumWidth: parent.width
Layout.minimumHeight: 14
SimplifiedControls.RadioButton {
id: controlsAdvanced
text: "Advanced"
ButtonGroup.group: controlsButtonGroup
checked: MyAvatar.getControlScheme() === 2
onClicked: {
MyAvatar.setControlScheme(2);
}
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
height: 14
}
SimplifiedControls.Slider {
id: controlsAdvancedMovementSpeed
anchors.top: parent.top
anchors.topMargin: 4 // For perfect alignment
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 300
height: 14
labelText: "Movement Speed"
labelTextColor: simplifiedUI.colors.text.darkGrey
from: 3
to: 30
defaultValue: 6
value: MyAvatar.analogPlusWalkSpeed
live: true
onValueChanged: {
if (MyAvatar.analogPlusWalkSpeed != controlsAdvancedMovementSpeed.value) {
MyAvatar.analogPlusWalkSpeed = controlsAdvancedMovementSpeed.value;
}
}
}
}
}
}
ColumnLayout {
id: micControlsContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: micControlsTitle
text: "Default Mute Controls"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ColumnLayout {
id: micControlsSwitchGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
SimplifiedControls.Switch {
id: muteMicrophoneSwitch
width: parent.width
height: 18
labelTextOn: "Mute Microphone"
checked: AudioScriptingInterface.mutedHMD
onClicked: {
AudioScriptingInterface.mutedHMD = !AudioScriptingInterface.mutedHMD;
}
}
SimplifiedControls.Switch {
id: pushToTalkSwitch
width: parent.width
height: 18
labelTextOn: "Push to Talk - Press and Hold Grip Triggers to Talk"
checked: AudioScriptingInterface.pushToTalkHMD
onClicked: {
AudioScriptingInterface.pushToTalkHMD = !AudioScriptingInterface.pushToTalkHMD;
}
}
}
}
ColumnLayout {
id: inputDeviceContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: inputDeviceTitle
text: "Which input device?"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ButtonGroup { id: inputDeviceButtonGroup }
ListView {
id: inputDeviceListView
anchors.left: parent.left
anchors.right: parent.right
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
clip: true
model: AudioScriptingInterface.devices.input
delegate: Item {
width: parent.width
height: inputDeviceCheckbox.height
SimplifiedControls.RadioButton {
id: inputDeviceCheckbox
anchors.left: parent.left
width: parent.width - inputLevel.width
checked: selectedHMD
text: model.devicename
ButtonGroup.group: inputDeviceButtonGroup
onClicked: {
AudioScriptingInterface.setStereoInput(false); // the next selected audio device might not support stereo
AudioScriptingInterface.setInputDevice(model.info, true); // `true` argument for HMD mode setting
}
}
SimplifiedControls.InputPeak {
id: inputLevel
showMuted: AudioScriptingInterface.mutedHMD
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
peak: model.peak
visible: AudioScriptingInterface.devices.input.peakValuesAvailable
}
}
}
SimplifiedControls.Button {
property bool audioLoopedBack: AudioScriptingInterface.getLocalEcho()
function startAudioLoopback() {
if (!audioLoopedBack) {
audioLoopedBack = true;
AudioScriptingInterface.setLocalEcho(true);
}
}
function stopAudioLoopback() {
if (audioLoopedBack) {
audioLoopedBack = false;
AudioScriptingInterface.setLocalEcho(false);
}
}
Timer {
id: loopbackTimer
interval: 8000
running: false
repeat: false
onTriggered: {
stopAudioLoopback();
}
}
id: testYourMicButton
enabled: HMD.active
anchors.left: parent.left
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
width: 160
height: 32
text: audioLoopedBack ? "STOP TESTING" : "TEST YOUR MIC"
onClicked: {
if (audioLoopedBack) {
loopbackTimer.stop();
stopAudioLoopback();
} else {
loopbackTimer.restart();
startAudioLoopback();
}
}
}
}
ColumnLayout {
id: outputDeviceContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
id: outputDeviceTitle
text: "Which output device?"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
ButtonGroup { id: outputDeviceButtonGroup }
ListView {
id: outputDeviceListView
anchors.left: parent.left
anchors.right: parent.right
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
clip: true
model: AudioScriptingInterface.devices.output
delegate: Item {
width: parent.width
height: outputDeviceCheckbox.height
SimplifiedControls.RadioButton {
id: outputDeviceCheckbox
anchors.left: parent.left
width: parent.width
checked: selectedDesktop
text: model.devicename
ButtonGroup.group: outputDeviceButtonGroup
onClicked: {
AudioScriptingInterface.setOutputDevice(model.info, true); // `false` argument for Desktop mode setting
}
}
}
}
SimplifiedControls.Button {
property var sound: null
property var sample: null
property bool isPlaying: false
function createSampleSound() {
sound = ApplicationInterface.getSampleSound();
sample = null;
}
function playSound() {
// FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap
// FIXME: AudioScriptingInterface.playSystemSound should not require position
if (sample === null && !isPlaying) {
sample = AudioScriptingInterface.playSystemSound(sound, MyAvatar.qmlPosition);
isPlaying = true;
sample.finished.connect(reset);
}
}
function stopSound() {
if (sample && isPlaying) {
sample.stop();
}
}
function reset() {
sample.finished.disconnect(reset);
isPlaying = false;
sample = null;
}
Component.onCompleted: createSampleSound();
Component.onDestruction: stopSound();
onVisibleChanged: {
if (!visible) {
stopSound();
}
}
id: testYourSoundButton
enabled: HMD.active
anchors.left: parent.left
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
width: 160
height: 32
text: isPlaying ? "STOP TESTING" : "TEST YOUR SOUND"
onClicked: {
isPlaying ? stopSound() : playSound()
}
}
}
}
}

View file

@ -0,0 +1,223 @@
//
// SimplifiedConstants.qml
//
// Created by Zach Fox on 2019-05-02
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
QtObject {
readonly property QtObject colors: QtObject {
readonly property QtObject text: QtObject {
readonly property color almostWhite: "#FAFAFA"
readonly property color lightGrey: "#CCCCCC"
readonly property color darkGrey: "#8F8F8F"
readonly property color white: "#FFFFFF"
readonly property color lightBlue: "#00B4EF"
readonly property color lightBlueHover: "#3dcfff"
}
readonly property QtObject controls: QtObject {
readonly property QtObject radioButton: QtObject {
readonly property QtObject background: QtObject {
readonly property color startColor: "#828282"
readonly property real startPosition: 0.15
readonly property color endColor: "#6A6A6A"
readonly property real endPosition: 1.0
readonly property real disabledOpacity: 0.5
}
readonly property QtObject hover: QtObject {
readonly property color outerBorderColor: "#00B4EF"
readonly property color innerColor: "#00B4EF"
readonly property color innerBorderColor: "#36CDFF"
readonly property real innerOpacity: 0.5
}
readonly property QtObject active: QtObject {
readonly property color color: "#00B4EF"
}
readonly property QtObject checked: QtObject {
readonly property color innerColor: "#00B4EF"
readonly property color innerBorderColor: "#36CDFF"
}
}
readonly property QtObject slider: QtObject {
readonly property QtObject background: QtObject {
readonly property color empty: "#252525"
readonly property QtObject filled: QtObject {
readonly property color start: "#0093C5"
readonly property color finish: "#00B4EF"
}
}
readonly property QtObject handle: QtObject {
readonly property color disabledBorder: "#2A2A2A"
readonly property color enabledBorder: "#00B4EF"
readonly property QtObject disabled: QtObject {
readonly property color start: "#2A2A2A"
readonly property color finish: "#2A2A2A"
}
readonly property QtObject normal: QtObject {
readonly property color start: "#828282"
readonly property color finish: "#6A6A6A"
}
readonly property QtObject hover: QtObject {
readonly property color start: "#48C7F4"
readonly property color finish: "#48C7F4"
}
readonly property QtObject pressed: QtObject {
readonly property color start: "#48C7F4"
readonly property color finish: "#48C7F4"
readonly property color border: "#00B4EF"
}
}
}
readonly property QtObject simplifiedSwitch: QtObject {
readonly property QtObject background: QtObject {
readonly property color disabled: "#616161"
readonly property color off: "#616161"
readonly property color hover: "#616161"
readonly property color pressed: "#616161"
readonly property color on: "#ffffff"
}
readonly property QtObject handle: QtObject {
readonly property color disabled: "#616161"
readonly property color off: "#E6E6E6"
readonly property color hover: "#48C7F4"
readonly property color active: "#48C7F4"
readonly property color activeBorder: "#00B4EF"
readonly property color on: "#00B4EF"
readonly property color checkedBorder: "#36CDFF"
}
readonly property QtObject text: QtObject {
readonly property color off: "#8F8F8F"
readonly property color on: "#ffffff"
}
}
readonly property QtObject button: QtObject {
readonly property QtObject background: QtObject {
readonly property color disabled: "#191919"
readonly property color enabled: "#191919"
readonly property color hover: "#00B4EF"
readonly property color active: "#00B4EF"
}
readonly property QtObject border: QtObject {
readonly property color disabled: "#8F8F8F"
readonly property color enabled: "#FFFFFF"
readonly property color hover: "#FFFFFF"
readonly property color active: "#FFFFFF"
}
readonly property QtObject text: QtObject {
readonly property color disabled: "#8F8F8F"
readonly property color enabled: "#FFFFFF"
}
}
readonly property QtObject outputVolumeButton: QtObject {
readonly property QtObject text: QtObject {
readonly property color muted: "#b20012"
readonly property color noisy: "#FFFFFF"
}
}
readonly property QtObject inputVolumeButton: QtObject {
readonly property QtObject text: QtObject {
readonly property color muted: "#b20012"
readonly property color noisy: "#FFFFFF"
}
}
readonly property QtObject checkBox: QtObject {
readonly property QtObject background: QtObject {
readonly property color disabled: "#464646"
readonly property color active: "#00B4EF"
readonly property color enabled: "#767676"
}
readonly property QtObject border: QtObject {
readonly property color hover: "#00B4EF"
}
readonly property QtObject innerBox: QtObject {
readonly property color border: "#36CDFF"
readonly property color background: "#00B4EF"
}
}
readonly property QtObject textField: QtObject {
readonly property color normal: Qt.rgba(1, 1, 1, 0.3)
readonly property color hover: "#FFFFFF"
readonly property color focus: "#FFFFFF"
}
}
readonly property color darkSeparator: "#595959"
readonly property color darkBackground: "#1A1A1A"
readonly property color darkBackgroundHighlight: "#575757"
readonly property color highlightOnDark: Qt.rgba(1, 1, 1, 0.2)
readonly property color white: "#FFFFFF"
}
readonly property QtObject glyphs: QtObject {
readonly property string gear: "@"
readonly property string editPencil: "\ue00d"
readonly property string playback_play: "\ue01d"
readonly property string stop_square: "\ue01e"
readonly property string hmd: "b"
readonly property string screen: "c"
readonly property string vol_0: "\ue00e"
readonly property string vol_1: "\ue00f"
readonly property string vol_2: "\ue010"
readonly property string vol_3: "\ue011"
readonly property string vol_4: "\ue012"
readonly property string vol_x_0: "\ue013"
readonly property string vol_x_1: "\ue014"
readonly property string vol_x_2: "\ue015"
readonly property string vol_x_3: "\ue016"
readonly property string vol_x_4: "\ue017"
readonly property string muted: "H"
readonly property string pencil: "\ue00d"
}
readonly property QtObject margins: QtObject {
readonly property QtObject controls: QtObject {
readonly property QtObject radioButton: QtObject {
readonly property int labelLeftMargin: 6
}
}
readonly property QtObject settings: QtObject {
property real subtitleTopMargin: 2
property real settingsGroupTopMargin: 10
property real spacingBetweenSettings: 48
}
}
readonly property QtObject sizes: QtObject {
readonly property QtObject controls: QtObject {
readonly property QtObject slider: QtObject {
readonly property int height: 16
readonly property int labelTextSize: 14
readonly property int backgroundHeight: 8
}
readonly property QtObject radioButton: QtObject {
readonly property int outerBorderWidth: 1
readonly property int innerBorderWidth: 1
}
readonly property QtObject simplifiedSwitch: QtObject {
readonly property int switchBackgroundHeight: 8
readonly property int switchBackgroundWidth: 30
readonly property int switchHandleInnerWidth: 12
readonly property int switchHandleOuterWidth: 16
readonly property int switchHandleBorderSize: 1
readonly property int labelTextSize: 14
readonly property int labelGlyphSize: 32
}
readonly property QtObject button: QtObject {
readonly property int borderWidth: 1
readonly property int textPadding: 16
readonly property int width: 160
readonly property int height: 32
readonly property int textSize: 14
}
readonly property QtObject textField: QtObject {
readonly property int editPencilPadding: 6
}
}
}
}

View file

@ -0,0 +1,106 @@
//
// Button.qml
//
// Created by Zach Fox on 2019-05-08
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3 as Original
import stylesUit 1.0 as HifiStylesUit
import "../simplifiedConstants" as SimplifiedConstants
import TabletScriptingInterface 1.0
Original.Button {
id: root
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
hoverEnabled: true
width: simplifiedUI.sizes.controls.button.width
height: simplifiedUI.sizes.controls.button.height
onHoveredChanged: {
if (hovered && enabled) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onFocusChanged: {
if (focus && enabled) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
if (enabled) {
Tablet.playSound(TabletEnums.ButtonClick);
}
}
background: Rectangle {
implicitWidth: root.width
implicitHeight: root.height
color: {
if (root.enabled) {
if (root.hovered) {
simplifiedUI.colors.controls.button.background.enabled
} else if (root.down) {
simplifiedUI.colors.controls.button.background.active
} else {
simplifiedUI.colors.controls.button.background.enabled
}
} else {
simplifiedUI.colors.controls.button.background.disabled
}
}
border.width: simplifiedUI.sizes.controls.button.borderWidth
border.color: root.enabled ? simplifiedUI.colors.controls.button.border.enabled : simplifiedUI.colors.controls.button.border.disabled
Item {
clip: true
visible: root.enabled
anchors.centerIn: parent
width: parent.width - parent.border.width * 2
height: parent.height - parent.border.width * 2
Rectangle {
z: -1
clip: true
width: root.down ? parent.width * 1.5 : (root.hovered ? parent.width * 9 / 10 : 0)
height: parent.height
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: -14
color: simplifiedUI.colors.controls.button.background.active
Behavior on width {
enabled: true
SmoothedAnimation { velocity: 400 }
}
transform: Matrix4x4 {
property real a: Math.PI / 4
matrix: Qt.matrix4x4(1, Math.tan(a), 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1)
}
}
}
}
contentItem: HifiStylesUit.GraphikMedium {
id: buttonText
topPadding: -2 // Necessary for proper alignment using Graphik Medium
wrapMode: Text.Wrap
color: enabled ? simplifiedUI.colors.controls.button.text.enabled : simplifiedUI.colors.controls.button.text.disabled
size: simplifiedUI.sizes.controls.button.textSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: root.text
}
}

View file

@ -0,0 +1,89 @@
//
// CheckBox.qml
//
// Created by Zach Fox on 2019-05-14
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3 as Original
import stylesUit 1.0 as HifiStylesUit
import "../simplifiedConstants" as SimplifiedConstants
import TabletScriptingInterface 1.0
Original.CheckBox {
id: root
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
property int colorScheme: hifi.colorSchemes.light
property string color: hifi.colors.lightGrayText
property alias checkBoxSize: checkBoxIndicator.width
property alias checkBoxRadius: checkBoxIndicator.radius
property alias checkSize: innerBox.width
property alias checkRadius: innerBox.radius
property bool wrapLabel: true
property alias labelFontFamily: checkBoxLabel.font.family
property alias labelFontSize: checkBoxLabel.font.pixelSize
property alias labelFontWeight: checkBoxLabel.font.weight
focusPolicy: Qt.ClickFocus
hoverEnabled: true
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
}
onHoveredChanged: {
if (hovered) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
indicator: Rectangle {
id: checkBoxIndicator
width: 14
height: width
radius: 4
y: parent.height / 2 - height / 2
color: root.enabled ?
(root.pressed ? simplifiedUI.colors.controls.checkBox.background.active : simplifiedUI.colors.controls.checkBox.background.enabled) :
simplifiedUI.colors.controls.checkBox.background.disabled
border.width: root.hovered ? 2 : 0
border.color: simplifiedUI.colors.controls.checkBox.border.hover
Rectangle {
id: innerBox
visible: root.hovered || root.checked
opacity: root.hovered ? 0.3 : 1.0
anchors.centerIn: parent
width: checkBoxIndicator.width - 4
height: width
radius: 2
color: simplifiedUI.colors.controls.checkBox.innerBox.background
border.width: 1
border.color: simplifiedUI.colors.controls.checkBox.innerBox.border
}
}
contentItem: Text {
id: checkBoxLabel
text: root.text
color: root.color
font.family: "Graphik"
font.pixelSize: 14
font.weight: Font.DemiBold
x: 2
verticalAlignment: Text.AlignVCenter
wrapMode: root.wrapLabel ? Text.Wrap : Text.NoWrap
elide: root.wrapLabel ? Text.ElideNone : Text.ElideRight
leftPadding: root.indicator.width + root.spacing
}
}

View file

@ -0,0 +1,101 @@
//
// InputPeak.qml
//
// Created by Zach Pomerantz on 6/20/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtGraphicalEffects 1.0
Item {
property var peak
property alias showMuted: status.visible
width: 70
height: 8
QtObject {
id: colors
readonly property string unmuted: "#FFF"
readonly property string muted: "#E2334D"
readonly property string gutter: "#575757"
readonly property string greenStart: "#39A38F"
readonly property string greenEnd: "#1FC6A6"
readonly property string yellow: "#C0C000"
readonly property string red: colors.muted
readonly property string fill: "#55000000"
}
Text {
id: status
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
visible: false
color: colors.muted
text: "MUTED"
font.pointSize: 10
}
Item {
id: bar
width: parent.width
height: parent.height
anchors { fill: parent }
visible: !status.visible
Rectangle { // base
radius: 4
anchors { fill: parent }
color: colors.gutter
}
Rectangle { // mask
id: mask
width: parent.width * peak
radius: 5
anchors {
bottom: parent.bottom
bottomMargin: 0
top: parent.top
topMargin: 0
left: parent.left
leftMargin: 0
}
}
LinearGradient {
anchors { fill: mask }
source: mask
start: Qt.point(0, 0)
end: Qt.point(bar.width, 0)
gradient: Gradient {
GradientStop {
position: 0
color: colors.greenStart
}
GradientStop {
position: 0.5
color: colors.greenEnd
}
GradientStop {
position: 1
color: colors.yellow
}
}
}
}
}

View file

@ -0,0 +1,103 @@
//
// RadioButton.qml
//
// Created by Zach Fox on 2019-05-06
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3
import TabletScriptingInterface 1.0
import "../simplifiedConstants" as SimplifiedConstants
RadioButton {
id: root
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
padding: 0
property alias labelTextColor: radioButtonLabel.color
property alias labelFontSize: radioButtonLabel.font.pixelSize
property int radioButtonRadius: 14
property int labelLeftMargin: simplifiedUI.margins.controls.radioButton.labelLeftMargin
property bool wrapLabel: true
readonly property int checkSize: Math.max(root.radioButtonRadius - 8, 10)
focusPolicy: Qt.ClickFocus
hoverEnabled: true
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
}
onHoveredChanged: {
if (hovered) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
indicator: Rectangle {
id: radioButtonIndicator
implicitWidth: root.radioButtonRadius
implicitHeight: root.radioButtonRadius
radius: root.radioButtonRadius
y: parent.height / 2 - height / 2
border.width: root.hovered ? simplifiedUI.sizes.controls.radioButton.outerBorderWidth : 0
border.color: simplifiedUI.colors.controls.radioButton.hover.outerBorderColor
opacity: root.disabled ? 0.5 : 1.0
gradient: Gradient {
GradientStop {
position: simplifiedUI.colors.controls.radioButton.background.startPosition
color: simplifiedUI.colors.controls.radioButton.background.startColor
}
GradientStop {
position: simplifiedUI.colors.controls.radioButton.background.endPosition
color: simplifiedUI.colors.controls.radioButton.background.endColor
}
}
Rectangle {
id: innerBox
visible: root.checked || root.hovered
anchors.centerIn: parent
width: root.checkSize
height: width
radius: checkSize / 2
border.width: simplifiedUI.sizes.controls.radioButton.innerBorderWidth
border.color: root.hovered ? simplifiedUI.colors.controls.radioButton.hover.innerBorderColor : simplifiedUI.colors.controls.radioButton.checked.innerBorderColor
color: root.hovered ? simplifiedUI.colors.controls.radioButton.hover.innerColor : simplifiedUI.colors.controls.radioButton.hover.innerColor
opacity: root.hovered ? simplifiedUI.colors.controls.radioButton.hover.innerOpacity : 1.0
}
Rectangle {
id: pressedBox
visible: root.pressed
width: parent.width
height: parent.height
radius: parent.radius
anchors.centerIn: parent
color: simplifiedUI.colors.controls.radioButton.active.color
}
}
contentItem: Text {
id: radioButtonLabel
height: root.radioButtonRadius
font.pixelSize: 14
font.family: "Graphik"
font.weight: Font.Normal
text: root.text
color: simplifiedUI.colors.text.white
x: 2
wrapMode: root.wrapLabel ? Text.Wrap : Text.NoWrap
elide: root.wrapLabel ? Text.ElideNone : Text.ElideRight
enabled: root.enabled
verticalAlignment: Text.AlignVCenter
leftPadding: radioButtonIndicator.width + root.labelLeftMargin
}
}

View file

@ -0,0 +1,126 @@
//
// Slider.qml
//
// Created by Zach Fox on 2019-05-06
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3
import stylesUit 1.0 as HifiStylesUit
import TabletScriptingInterface 1.0
import "../simplifiedConstants" as SimplifiedConstants
Item {
id: root
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
property alias labelText: sliderText.text
property alias labelTextSize: sliderText.size
property alias labelTextColor: sliderText.color
property alias value: sliderControl.value
property alias from: sliderControl.from
property alias to: sliderControl.to
property alias live: sliderControl.live
property alias stepSize: sliderControl.stepSize
property alias snapMode: sliderControl.snapMode
property real defaultValue: 0.0
HifiStylesUit.GraphikRegular {
id: sliderText
text: ""
anchors.right: sliderControl.left
anchors.rightMargin: 8
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: -4 // Necessary for vertical alignment
anchors.bottom: parent.bottom
horizontalAlignment: Text.AlignRight
visible: sliderText.text != ""
color: simplifiedUI.colors.text.white
size: simplifiedUI.sizes.controls.slider.labelTextSize
}
Slider {
id: sliderControl
height: simplifiedUI.sizes.controls.slider.height
width: root.width * 0.6
enabled: root.enabled
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onPressedChanged: {
if (pressed) {
Tablet.playSound(TabletEnums.ButtonClick);
}
}
onHoveredChanged: {
if (hovered) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
background: Rectangle {
id: sliderBackground
width: sliderControl.width - sliderHandle.width
height: simplifiedUI.sizes.controls.slider.backgroundHeight
x: sliderHandle.width / 2
y: sliderControl.height / 2 - height / 2
radius: height / 2
color: simplifiedUI.colors.controls.slider.background.empty
Rectangle {
width: sliderControl.visualPosition * sliderBackground.width
height: parent.height
radius: height / 2
gradient: Gradient {
GradientStop { position: 0.0; color: simplifiedUI.colors.controls.slider.background.filled.start }
GradientStop { position: 1.0; color: simplifiedUI.colors.controls.slider.background.filled.finish }
}
}
}
handle: Rectangle {
id: sliderHandle
width: sliderControl.height
height: width
x: sliderControl.visualPosition * sliderBackground.width
y: sliderControl.height / 2 - height / 2
radius: height / 2
color: "#000000"
border.width: 1
border.color: sliderControl.hovered || sliderControl.pressed ? simplifiedUI.colors.controls.slider.handle.enabledBorder : simplifiedUI.colors.controls.slider.handle.disabledBorder
Rectangle {
visible: root.enabled
height: sliderControl.pressed ? parent.height : parent.height - 4
width: height
radius: height / 2
anchors.centerIn: parent
gradient: Gradient {
GradientStop {
position: 0.2
color: sliderControl.enabled ? (sliderControl.hovered ? simplifiedUI.colors.controls.slider.handle.hover.start :
(sliderControl.pressed
? (simplifiedUI.colors.controls.slider.handle.pressed.start)
: (simplifiedUI.colors.controls.slider.handle.normal.start))) : simplifiedUI.colors.controls.slider.handle.disabled.start
}
GradientStop {
position: 1.0
color: sliderControl.enabled ? (sliderControl.hovered ? simplifiedUI.colors.controls.slider.handle.hover.finish :
(sliderControl.pressed
? (simplifiedUI.colors.controls.slider.handle.pressed.finish)
: (simplifiedUI.colors.controls.slider.handle.normal.finish))) : simplifiedUI.colors.controls.slider.handle.disabled.finish
}
}
}
}
}
}

View file

@ -0,0 +1,234 @@
//
// Switch.qml
//
// Created by Zach Fox on 2019-05-08
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3 as Original
import stylesUit 1.0 as HifiStylesUit
import TabletScriptingInterface 1.0
import "../simplifiedConstants" as SimplifiedConstants
Item {
id: root
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
property alias switchWidth: switchBackground.width
property alias labelTextOff: labelOff.text
property alias labelTextOffSize: labelOff.size
property alias labelGlyphOffText: labelGlyphOff.text
property alias labelGlyphOffSize: labelGlyphOff.size
property alias labelTextOn: labelOn.text
property alias labelTextOnSize: labelOn.size
property alias labelGlyphOnText: labelGlyphOn.text
property alias labelGlyphOnSize: labelGlyphOn.size
property alias checked: originalSwitch.checked
property string backgroundOnColor: "#252525"
signal clicked
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
}
Original.Switch {
id: originalSwitch
enabled: root.enabled
focusPolicy: Qt.ClickFocus
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: labelOff.text === "" ? undefined : parent.horizontalCenter
anchors.left: labelOff.text === "" ? parent.left : undefined
anchors.leftMargin: (outerSwitchHandle.width - innerSwitchHandle.width) / 2
width: simplifiedUI.sizes.controls.simplifiedSwitch.switchBackgroundWidth
hoverEnabled: true
function changeColor() {
if (!originalSwitch.enabled) {
innerSwitchHandle.color = simplifiedUI.colors.controls.simplifiedSwitch.handle.disabled;
return;
}
if (originalSwitch.checked) {
if (originalSwitch.hovered) {
innerSwitchHandle.color = simplifiedUI.colors.controls.simplifiedSwitch.handle.hover;
} else {
innerSwitchHandle.color = simplifiedUI.colors.controls.simplifiedSwitch.handle.on;
}
} else {
if (originalSwitch.hovered) {
innerSwitchHandle.color = simplifiedUI.colors.controls.simplifiedSwitch.handle.hover;
} else {
innerSwitchHandle.color = simplifiedUI.colors.controls.simplifiedSwitch.handle.off;
}
}
}
onCheckedChanged: {
root.checkedChanged();
Tablet.playSound(TabletEnums.ButtonClick);
originalSwitch.changeColor();
}
onClicked: {
root.clicked();
originalSwitch.changeColor();
}
onHoveredChanged: {
if (hovered) {
Tablet.playSound(TabletEnums.ButtonHover);
}
originalSwitch.changeColor();
}
background: Rectangle {
id: switchBackground
anchors.verticalCenter: parent.verticalCenter
color: originalSwitch.checked ? simplifiedUI.colors.controls.simplifiedSwitch.background.on : simplifiedUI.colors.controls.simplifiedSwitch.background.off
width: originalSwitch.width
height: simplifiedUI.sizes.controls.simplifiedSwitch.switchBackgroundHeight
radius: height/2
}
indicator: Item {
anchors.verticalCenter: parent.verticalCenter
width: simplifiedUI.sizes.controls.simplifiedSwitch.switchHandleOuterWidth
height: width
x: originalSwitch.visualPosition * switchBackground.width - (innerSwitchHandle.width * (originalSwitch.checked ? 1 : 0)) - ((outerSwitchHandle.width - innerSwitchHandle.width) / 2)
Behavior on x {
enabled: !originalSwitch.down
SmoothedAnimation { velocity: 200 }
}
Rectangle {
id: outerSwitchHandle
visible: originalSwitch.hovered
anchors.centerIn: parent
width: simplifiedUI.sizes.controls.simplifiedSwitch.switchHandleOuterWidth
height: width
radius: width/2
color: "transparent"
border.width: simplifiedUI.sizes.controls.simplifiedSwitch.switchHandleBorderSize
border.color: simplifiedUI.colors.controls.simplifiedSwitch.handle.activeBorder
}
Rectangle {
id: innerSwitchHandle
anchors.centerIn: parent
width: simplifiedUI.sizes.controls.simplifiedSwitch.switchHandleInnerWidth
height: width
radius: width/2
color: simplifiedUI.colors.controls.simplifiedSwitch.handle.off
border.width: originalSwitch.pressed || originalSwitch.checked ? simplifiedUI.sizes.controls.simplifiedSwitch.switchHandleBorderSize : 0
border.color: originalSwitch.pressed ? simplifiedUI.colors.controls.simplifiedSwitch.handle.activeBorder : simplifiedUI.colors.controls.simplifiedSwitch.handle.checkedBorder
Component.onCompleted: {
originalSwitch.changeColor();
}
}
}
}
// OFF Label
Item {
anchors.right: originalSwitch.left
anchors.rightMargin: 10
anchors.top: parent.top
anchors.bottom: parent.bottom
HifiStylesUit.GraphikRegular {
id: labelOff
text: ""
size: simplifiedUI.sizes.controls.simplifiedSwitch.labelTextSize
color: originalSwitch.checked ? simplifiedUI.colors.controls.simplifiedSwitch.text.off : simplifiedUI.colors.controls.simplifiedSwitch.text.on
anchors.top: parent.top
anchors.topMargin: -2 // Necessary for text alignment
anchors.bottom: parent.bottom
anchors.right: parent.right
width: paintedWidth
verticalAlignment: Text.AlignVCenter
}
HifiStylesUit.HiFiGlyphs {
id: labelGlyphOff
text: ""
size: simplifiedUI.sizes.controls.simplifiedSwitch.labelGlyphSize
color: labelOff.color
anchors.top: parent.top
anchors.topMargin: 2
anchors.right: labelOff.left
anchors.rightMargin: 4
}
MouseArea {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: labelGlyphOff.left
anchors.right: labelOff.right
onClicked: {
if (labelOn.text === "" && labelGlyphOn.text === "") {
originalSwitch.checked = !originalSwitch.checked;
} else {
originalSwitch.checked = false;
}
root.clicked();
}
}
}
// ON Label
Item {
anchors.left: originalSwitch.right
anchors.leftMargin: 10
anchors.top: parent.top
anchors.bottom: parent.bottom
HifiStylesUit.GraphikRegular {
id: labelOn
text: ""
size: simplifiedUI.sizes.controls.simplifiedSwitch.labelTextSize
color: originalSwitch.checked ? simplifiedUI.colors.controls.simplifiedSwitch.text.on : simplifiedUI.colors.controls.simplifiedSwitch.text.off
anchors.top: parent.top
anchors.topMargin: -2 // Necessary for text alignment
anchors.left: parent.left
width: paintedWidth
height: parent.height
verticalAlignment: Text.AlignVCenter
}
HifiStylesUit.HiFiGlyphs {
id: labelGlyphOn
text: ""
size: simplifiedUI.sizes.controls.simplifiedSwitch.labelGlyphSize
color: labelOn.color
anchors.top: parent.top
anchors.topMargin: 2
anchors.left: labelOn.right
anchors.leftMargin: 4
}
MouseArea {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: labelOn.left
anchors.right: labelGlyphOn.right
onClicked: {
if (labelOff.text === "" && labelGlyphOff.text === "") {
originalSwitch.checked = !originalSwitch.checked;
} else {
originalSwitch.checked = true;
}
root.clicked();
}
}
}
}

View file

@ -0,0 +1,75 @@
//
// TextField.qml
//
// Created by Zach Fox on 2019-05-06
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3
import TabletScriptingInterface 1.0
import "../simplifiedConstants" as SimplifiedConstants
import stylesUit 1.0 as HifiStylesUit
TextField {
id: root
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
color: simplifiedUI.colors.text.white
font.family: "Graphik Medium"
font.pixelSize: 22
selectionColor: simplifiedUI.colors.text.white
selectedTextColor: simplifiedUI.colors.text.darkGrey
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignLeft
autoScroll: false
hoverEnabled: true
leftPadding: 0
rightPadding: editPencil.implicitWidth + simplifiedUI.sizes.controls.textField.editPencilPadding
onFocusChanged: {
if (focus) {
Tablet.playSound(TabletEnums.ButtonClick);
}
}
onHoveredChanged: {
if (hovered) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
background: Item {
anchors.fill: parent
Rectangle {
id: bottomRectangle
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: root.focus ? simplifiedUI.colors.controls.textField.focus :
(root.hovered ? simplifiedUI.colors.controls.textField.hover : simplifiedUI.colors.controls.textField.normal)
}
HifiStylesUit.HiFiGlyphs {
id: editPencil
text: simplifiedUI.glyphs.pencil
// Text Size
size: root.font.pixelSize * 1.5
// Anchors
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
// Style
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: bottomRectangle.color
}
}
}

View file

@ -0,0 +1,352 @@
//
// SimplifiedTopBar.qml
//
// Created by Zach Fox on 2019-05-02
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import "../simplifiedConstants" as SimplifiedConstants
import "../inputDeviceButton" as InputDeviceButton
import stylesUit 1.0 as HifiStylesUit
import TabletScriptingInterface 1.0
import QtGraphicalEffects 1.0
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
Rectangle {
id: root
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
color: simplifiedUI.colors.darkBackground
anchors.fill: parent
property bool inventoryFullyReceived: false
Component.onCompleted: {
Commerce.getLoginStatus();
}
Connections {
target: MyAvatar
onSkeletonModelURLChanged: {
root.updatePreviewUrl();
}
}
Connections {
target: Commerce
onLoginStatusResult: {
if (inventoryFullyReceived) {
return;
}
if (isLoggedIn) {
Commerce.getWalletStatus();
} else {
// Show some error to the user
}
}
onWalletStatusResult: {
if (inventoryFullyReceived) {
return;
}
if (walletStatus === 5) {
topBarInventoryModel.getFirstPage();
} else {
// Show some error to the user
}
}
onInventoryResult: {
if (inventoryFullyReceived) {
return;
}
topBarInventoryModel.handlePage(result.status !== "success" && result.message, result);
root.updatePreviewUrl();
// I _should_ be able to do `if (currentPageToRetrieve > -1)` here, but the
// inventory endpoint doesn't return `response.total_pages`, so the PSFListModel doesn't
// know when to automatically stop fetching new pages.
// This will result in fetching one extra page than is necessary, but that's not a big deal.
if (result.data.assets.length > 0) {
topBarInventoryModel.getNextPage();
} else {
inventoryFullyReceived = true;
}
}
}
HifiModels.PSFListModel {
id: topBarInventoryModel
itemsPerPage: 8
listModelName: 'inventory'
getPage: function () {
var editionFilter = "";
var primaryFilter = "avatar";
var titleFilter = "";
Commerce.inventory(
editionFilter,
primaryFilter,
titleFilter,
topBarInventoryModel.currentPageToRetrieve,
topBarInventoryModel.itemsPerPage
);
}
processPage: function(data) {
data.assets.forEach(function (item) {
if (item.status.length > 1) { console.warn("Unrecognized inventory status", item); }
item.status = item.status[0];
});
return data.assets;
}
}
Item {
id: avatarButtonContainer
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 16
width: 48
height: width
AnimatedImage {
visible: avatarButtonImage.source === ""
anchors.centerIn: parent
width: parent.width - 10
height: width
source: "../images/loading.gif"
}
Image {
id: avatarButtonImage
visible: source !== ""
source: ""
anchors.centerIn: parent
width: parent.width - 10
height: width
mipmap: true
fillMode: Image.PreserveAspectCrop
layer.enabled: true
layer.effect: OpacityMask {
maskSource: mask
}
MouseArea {
id: avatarButtonImageMouseArea
anchors.fill: parent
hoverEnabled: enabled
onEntered: {
Tablet.playSound(TabletEnums.ButtonHover);
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
if (Account.loggedIn) {
sendToScript({
"source": "SimplifiedTopBar.qml",
"method": "toggleAvatarApp"
});
} else {
DialogsManager.showLoginDialog();
}
}
}
}
Rectangle {
z: -1
id: borderMask
width: avatarButtonImageMouseArea.containsMouse ? avatarButtonImage.width + 4 : avatarButtonImage.width - 4
height: width
radius: width
anchors.centerIn: avatarButtonImage
color: "#FFFFFF"
Behavior on width {
enabled: true
SmoothedAnimation { velocity: 80 }
}
}
Rectangle {
id: mask
anchors.fill: avatarButtonImage
radius: avatarButtonImage.width
visible: false
}
}
InputDeviceButton.InputDeviceButton {
id: inputDeviceButton
anchors.verticalCenter: parent.verticalCenter
anchors.left: avatarButtonContainer.right
anchors.leftMargin: 8
}
Item {
id: outputDeviceButtonContainer
anchors.verticalCenter: parent.verticalCenter
anchors.left: inputDeviceButton.right
anchors.leftMargin: 24
width: 20
height: width
HifiStylesUit.HiFiGlyphs {
property bool outputMuted: false
id: outputDeviceButton
text: (outputDeviceButton.outputMuted ? simplifiedUI.glyphs.vol_0 : simplifiedUI.glyphs.vol_3)
color: (outputDeviceButton.outputMuted ? simplifiedUI.colors.controls.outputVolumeButton.text.muted : simplifiedUI.colors.controls.outputVolumeButton.text.noisy)
opacity: outputDeviceButtonMouseArea.containsMouse ? 1.0 : 0.7
size: 32
anchors.centerIn: parent
width: parent.width
height: parent.height
horizontalAlignment: Text.AlignHCenter
MouseArea {
id: outputDeviceButtonMouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: {
Tablet.playSound(TabletEnums.ButtonHover);
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
outputDeviceButton.outputMuted = !outputDeviceButton.outputMuted;
sendToScript({
"source": "SimplifiedTopBar.qml",
"method": "setOutputMuted",
"data": {
"outputMuted": outputDeviceButton.outputMuted
}
});
}
}
}
}
Item {
id: hmdButtonContainer
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: settingsButtonContainer.left
anchors.rightMargin: 8
width: height
HifiStylesUit.HiFiGlyphs {
id: hmdGlyph
text: HMD.active ? simplifiedUI.glyphs.hmd : simplifiedUI.glyphs.screen
color: simplifiedUI.colors.text.white
opacity: hmdGlyphMouseArea.containsMouse ? 1.0 : 0.7
size: 40
anchors.centerIn: parent
width: 35
height: parent.height
horizontalAlignment: Text.AlignHCenter
MouseArea {
id: hmdGlyphMouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: {
Tablet.playSound(TabletEnums.ButtonHover);
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
// TODO: actually do this right and change the display plugin
HMD.active = !HMD.active;
}
}
}
}
Item {
id: settingsButtonContainer
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: 16
width: height
HifiStylesUit.HiFiGlyphs {
id: settingsGlyph
text: simplifiedUI.glyphs.gear
color: simplifiedUI.colors.text.white
opacity: settingsGlyphMouseArea.containsMouse ? 1.0 : 0.7
size: 40
anchors.centerIn: parent
width: 35
height: parent.height
horizontalAlignment: Text.AlignHCenter
MouseArea {
id: settingsGlyphMouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: {
Tablet.playSound(TabletEnums.ButtonHover);
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
sendToScript({
"source": "SimplifiedTopBar.qml",
"method": "toggleSettingsApp"
});
}
}
}
}
function updatePreviewUrl() {
var previewUrl = "";
var downloadUrl = "";
for (var i = 0; i < topBarInventoryModel.count; ++i) {
downloadUrl = topBarInventoryModel.get(i).download_url;
previewUrl = topBarInventoryModel.get(i).preview;
if (MyAvatar.skeletonModelURL === downloadUrl) {
avatarButtonImage.source = previewUrl;
return;
}
}
}
function fromScript(message) {
if (message.source !== "simplifiedUI.js") {
return;
}
switch (message.method) {
case "updateAvatarThumbnailURL":
avatarButtonImage.source = message.data.avatarThumbnailURL;
break;
case "updateOutputMuted":
outputDeviceButton.outputMuted = message.data.outputMuted;
break;
default:
console.log('SimplifiedTopBar.qml: Unrecognized message from JS');
break;
}
}
signal sendToScript(var message);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -85,23 +85,11 @@ TabBar {
NewEntityButton {
icon: "icons/create-icons/21-cube-01.svg"
text: "CUBE"
text: "SHAPE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked",
params: { buttonName: "newCubeButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/22-sphere-01.svg"
text: "SPHERE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked",
params: { buttonName: "newSphereButton" }
params: { buttonName: "newShapeButton" }
});
editTabView.currentIndex = 2
}

View file

@ -91,23 +91,11 @@ TabBar {
NewEntityButton {
icon: "icons/create-icons/21-cube-01.svg"
text: "CUBE"
text: "SHAPE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked",
params: { buttonName: "newCubeButton" }
});
editTabView.currentIndex = tabIndex.properties
}
}
NewEntityButton {
icon: "icons/create-icons/22-sphere-01.svg"
text: "SPHERE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked",
params: { buttonName: "newSphereButton" }
params: { buttonName: "newShapeButton" }
});
editTabView.currentIndex = tabIndex.properties
}

View file

@ -0,0 +1,21 @@
//
// GraphikMedium.qml
//
// Created by Wayne Chen on 3 May 2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
Text {
id: root
property real size: 32
font.pixelSize: size
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.family: "Graphik"
font.weight: Font.Medium
}

View file

@ -0,0 +1,20 @@
//
// GraphikRegular.qml
//
// Created by Wayne Chen on 2 May 2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
Text {
id: root
property real size: 32
font.pixelSize: size
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.family: "Graphik"
}

View file

@ -0,0 +1,21 @@
//
// GraphikSemiBold.qml
//
// Created by Wayne Chen on 2 May 2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
Text {
id: root
property real size: 32
font.pixelSize: size
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.family: "Graphik"
font.weight: Font.DemiBold
}

View file

@ -5,12 +5,16 @@ FiraSansRegular 1.0 FiraSansRegular.qml
FiraSansSemiBold 1.0 FiraSansSemiBold.qml
HifiConstants 1.0 HifiConstants.qml
HiFiGlyphs 1.0 HiFiGlyphs.qml
GraphikMedium 1.0 GraphikMedium.qml
GraphikRegular 1.0 GraphikRegular.qml
GraphikSemiBold 1.0 GraphikSemiBold.qml
IconButton 1.0 IconButton.qml
InfoItem 1.0 InfoItem.qml
InputLabel 1.0 InputLabel.qml
ListItem 1.0 ListItem.qml
Logs 1.0 Logs.qml
OverlayTitle 1.0 OverlayTitle.qml
Rawline 1.0 Rawline.qml
RalewayBold 1.0 RalewayBold.qml
RalewayLight 1.0 RalewayLight.qml
RalewayRegular 1.0 RalewayRegular.qml
@ -19,4 +23,4 @@ SectionName 1.0 SectionName.qml
Separator 1.0 Separator.qml
ShortcutText 1.0 ShortcutText.qml
TabName 1.0 TabName.qml
TextFieldInput 1.0 TextFieldInput.qml
TextFieldInput 1.0 TextFieldInput.qml

View file

@ -250,17 +250,6 @@
#if defined(Q_OS_WIN)
#include <VersionHelpers.h>
#ifdef DEBUG_EVENT_QUEUE
// This is a HACK that uses private headers included with the qt source distrubution.
// To use this feature you need to add these directores to your include path:
// E:/Qt/5.10.1/Src/qtbase/include/QtCore/5.10.1/QtCore
// E:/Qt/5.10.1/Src/qtbase/include/QtCore/5.10.1
#define QT_BOOTSTRAPPED
#include <private/qthread_p.h>
#include <private/qobject_p.h>
#undef QT_BOOTSTRAPPED
#endif
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
extern "C" {
@ -1123,6 +1112,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Bold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-SemiBold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Cairo-SemiBold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Graphik-SemiBold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Graphik-Regular.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Graphik-Medium.ttf");
_window->setWindowTitle("High Fidelity Interface");
Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us
@ -3081,6 +3073,7 @@ void Application::initializeUi() {
QmlContextCallback commerceCallback = [](QQmlContext* context) {
context->setContextProperty("Commerce", DependencyManager::get<QmlCommerce>().data());
};
OffscreenQmlSurface::addWhitelistContextHandler({
QUrl{ "hifi/commerce/checkout/Checkout.qml" },
QUrl{ "hifi/commerce/common/CommerceLightbox.qml" },
@ -3106,6 +3099,8 @@ void Application::initializeUi() {
QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" },
QUrl{ "hifi/tablet/TabletMenu.qml" },
QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
QUrl{ "hifi/simplifiedUI/avatarApp/AvatarApp.qml" },
QUrl{ "hifi/simplifiedUI/topBar/SimplifiedTopBar.qml" },
}, commerceCallback);
QmlContextCallback marketplaceCallback = [](QQmlContext* context) {
@ -4029,24 +4024,6 @@ bool Application::handleFileOpenEvent(QFileOpenEvent* fileEvent) {
return false;
}
#ifdef DEBUG_EVENT_QUEUE
static int getEventQueueSize(QThread* thread) {
auto threadData = QThreadData::get2(thread);
QMutexLocker locker(&threadData->postEventList.mutex);
return threadData->postEventList.size();
}
static void dumpEventQueue(QThread* thread) {
auto threadData = QThreadData::get2(thread);
QMutexLocker locker(&threadData->postEventList.mutex);
qDebug() << "Event list, size =" << threadData->postEventList.size();
for (auto& postEvent : threadData->postEventList) {
QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None);
qDebug() << " " << type;
}
}
#endif // DEBUG_EVENT_QUEUE
bool Application::notify(QObject * object, QEvent * event) {
if (thread() == QThread::currentThread()) {
PROFILE_RANGE_IF_LONGER(app, "notify", 2)
@ -4082,14 +4059,16 @@ bool Application::event(QEvent* event) {
case ApplicationEvent::Idle:
idle();
#ifdef DEBUG_EVENT_QUEUE
#ifdef DEBUG_EVENT_QUEUE_DEPTH
// The event queue may very well grow beyond 400, so
// this code should only be enabled on local builds
{
int count = getEventQueueSize(QThread::currentThread());
int count = ::hifi::qt::getEventQueueSize(QThread::currentThread());
if (count > 400) {
dumpEventQueue(QThread::currentThread());
::hifi::qt::dumpEventQueue(QThread::currentThread());
}
}
#endif // DEBUG_EVENT_QUEUE
#endif // DEBUG_EVENT_QUEUE_DEPTH
_pendingIdleEvent.store(false);

View file

@ -370,6 +370,8 @@ class MyAvatar : public Avatar {
Q_PROPERTY(QUuid SELF_ID READ getSelfID CONSTANT)
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
Q_PROPERTY(float analogPlusWalkSpeed READ getAnalogPlusWalkSpeed WRITE setAnalogPlusWalkSpeed);
Q_PROPERTY(float analogPlusSprintSpeed READ getAnalogPlusSprintSpeed WRITE setAnalogPlusSprintSpeed);
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed);
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed);
Q_PROPERTY(bool isInSittingState READ getIsInSittingState WRITE setIsInSittingState);

View file

@ -317,13 +317,13 @@ void Ledger::accountSuccess(QNetworkReply* reply) {
const QByteArray locker = data["locker"].toString().toUtf8();
bool isOverride = wallet->wasSoftReset();
wallet->setSalt(salt);
wallet->setIv(iv);
wallet->setCKey(ckey);
if (!locker.isEmpty()) {
wallet->setWallet(locker);
wallet->setPassphrase("ACCOUNT"); // We only locker wallets that have been converted to account-based auth.
}
wallet->setSalt(salt);
QString keyStatus = "ok";
QStringList localPublicKeys = wallet->listPublicKeys();

View file

@ -313,6 +313,8 @@ Wallet::Wallet() {
walletScriptingInterface->setWalletStatus(status);
});
connect(ledger.data(), &Ledger::accountResult, this, &Wallet::sendChallengeOwnershipResponses);
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
getWalletStatus();
@ -823,88 +825,101 @@ bool Wallet::changePassphrase(const QString& newPassphrase) {
}
void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
_pendingChallenges.push_back(packet);
sendChallengeOwnershipResponses();
}
void Wallet::sendChallengeOwnershipResponses() {
if (_pendingChallenges.size() == 0 || getSalt().length() == 0) {
return;
}
auto nodeList = DependencyManager::get<NodeList>();
// With EC keys, we receive a nonce from the metaverse server, which is signed
// here with the private key and returned. Verification is done at server.
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
int status;
int idByteArraySize;
int textByteArraySize;
int challengingNodeUUIDByteArraySize;
packet->readPrimitive(&idByteArraySize);
packet->readPrimitive(&textByteArraySize); // returns a cast char*, size
if (challengeOriginatedFromClient) {
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
}
// "encryptedText" is now a series of random bytes, a nonce
QByteArray id = packet->read(idByteArraySize);
QByteArray text = packet->read(textByteArraySize);
QByteArray challengingNodeUUID;
if (challengeOriginatedFromClient) {
challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize);
}
EC_KEY* ec = readKeys(keyFilePath());
QString sig;
if (ec) {
ERR_clear_error();
sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with
status = 1;
} else {
qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed.";
status = -1;
for (const auto& packet: _pendingChallenges) {
// With EC keys, we receive a nonce from the metaverse server, which is signed
// here with the private key and returned. Verification is done at server.
QString sig;
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
int status;
int idByteArraySize;
int textByteArraySize;
int challengingNodeUUIDByteArraySize;
packet->readPrimitive(&idByteArraySize);
packet->readPrimitive(&textByteArraySize); // returns a cast char*, size
if (challengeOriginatedFromClient) {
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
}
// "encryptedText" is now a series of random bytes, a nonce
QByteArray id = packet->read(idByteArraySize);
QByteArray text = packet->read(textByteArraySize);
QByteArray challengingNodeUUID;
if (challengeOriginatedFromClient) {
challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize);
}
if (ec) {
ERR_clear_error();
sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with
status = 1;
} else {
qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed.";
status = -1;
}
QByteArray textByteArray;
if (status > -1) {
textByteArray = sig.toUtf8();
}
textByteArraySize = textByteArray.size();
int idSize = id.size();
// setup the packet
Node& sendingNode = *nodeList->nodeWithLocalID(packet->getSourceID());
if (challengeOriginatedFromClient) {
auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
true);
textPacket->writePrimitive(idSize);
textPacket->writePrimitive(textByteArraySize);
textPacket->writePrimitive(challengingNodeUUIDByteArraySize);
textPacket->write(id);
textPacket->write(textByteArray);
textPacket->write(challengingNodeUUID);
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id;
nodeList->sendPacket(std::move(textPacket), sendingNode);
} else {
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true);
textPacket->writePrimitive(idSize);
textPacket->writePrimitive(textByteArraySize);
textPacket->write(id);
textPacket->write(textByteArray);
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id;
nodeList->sendPacket(std::move(textPacket), sendingNode);
}
if (status == -1) {
qCDebug(commerce) << "During entity ownership challenge, signing the text failed.";
long error = ERR_get_error();
if (error != 0) {
const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "EC error:" << error_str;
}
}
}
EC_KEY_free(ec);
QByteArray textByteArray;
if (status > -1) {
textByteArray = sig.toUtf8();
}
textByteArraySize = textByteArray.size();
int idSize = id.size();
// setup the packet
if (challengeOriginatedFromClient) {
auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
true);
textPacket->writePrimitive(idSize);
textPacket->writePrimitive(textByteArraySize);
textPacket->writePrimitive(challengingNodeUUIDByteArraySize);
textPacket->write(id);
textPacket->write(textByteArray);
textPacket->write(challengingNodeUUID);
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id;
nodeList->sendPacket(std::move(textPacket), *sendingNode);
} else {
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true);
textPacket->writePrimitive(idSize);
textPacket->writePrimitive(textByteArraySize);
textPacket->write(id);
textPacket->write(textByteArray);
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id;
nodeList->sendPacket(std::move(textPacket), *sendingNode);
}
if (status == -1) {
qCDebug(commerce) << "During entity ownership challenge, signing the text failed.";
long error = ERR_get_error();
if (error != 0) {
const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "EC error:" << error_str;
}
}
_pendingChallenges.clear();
}
void Wallet::account() {

View file

@ -94,6 +94,7 @@ signals:
private slots:
void handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void sendChallengeOwnershipResponses();
private:
friend class Ledger;
@ -104,6 +105,7 @@ private:
QByteArray _ckey;
QString* _passphrase { nullptr };
bool _isOverridingServer { false };
std::vector<QSharedPointer<ReceivedMessage>> _pendingChallenges;
bool writeWallet(const QString& newPassphrase = QString(""));
void updateImageProvider();

View file

@ -20,6 +20,7 @@
static AvatarInputs* INSTANCE{ nullptr };
Setting::Handle<bool> showAudioToolsSetting { QStringList { "AvatarInputs", "showAudioTools" }, true };
Setting::Handle<bool> showBubbleToolsSetting{ QStringList { "AvatarInputs", "showBubbleTools" }, true };
AvatarInputs* AvatarInputs::getInstance() {
if (!INSTANCE) {
@ -88,6 +89,15 @@ void AvatarInputs::setShowAudioTools(bool showAudioTools) {
emit showAudioToolsChanged(_showAudioTools);
}
void AvatarInputs::setShowBubbleTools(bool showBubbleTools) {
if (_showBubbleTools == showBubbleTools)
return;
_showBubbleTools = showBubbleTools;
showBubbleToolsSetting.set(_showAudioTools);
emit showBubbleToolsChanged(_showBubbleTools);
}
bool AvatarInputs::getIgnoreRadiusEnabled() const {
return DependencyManager::get<NodeList>()->getIgnoreRadiusEnabled();
}

View file

@ -35,6 +35,7 @@ class AvatarInputs : public QObject {
* @property {boolean} cameraMuted <em>Read-only.</em>
* @property {boolean} isHMD <em>Read-only.</em>
* @property {boolean} showAudioTools
* @property {boolean} showBubbleTools
*/
AI_PROPERTY(bool, cameraEnabled, false)
@ -42,6 +43,7 @@ class AvatarInputs : public QObject {
AI_PROPERTY(bool, isHMD, false)
Q_PROPERTY(bool showAudioTools READ showAudioTools WRITE setShowAudioTools NOTIFY showAudioToolsChanged)
Q_PROPERTY(bool showBubbleTools READ showBubbleTools WRITE setShowBubbleTools NOTIFY showBubbleToolsChanged)
Q_PROPERTY(bool ignoreRadiusEnabled READ getIgnoreRadiusEnabled NOTIFY ignoreRadiusEnabledChanged)
//Q_PROPERTY(bool enteredIgnoreRadius READ getEnteredIgnoreRadius NOTIFY enteredIgnoreRadiusChanged)
@ -58,6 +60,7 @@ public:
AvatarInputs(QObject* parent = nullptr);
void update();
bool showAudioTools() const { return _showAudioTools; }
bool showBubbleTools() const { return _showBubbleTools; }
bool getIgnoreRadiusEnabled() const;
//bool getEnteredIgnoreRadius() const;
@ -69,6 +72,12 @@ public slots:
*/
void setShowAudioTools(bool showAudioTools);
/**jsdoc
* @function AvatarInputs.setShowBubbleTools
* @param {boolean} showBubbleTools
*/
void setShowBubbleTools(bool showBubbleTools);
signals:
/**jsdoc
@ -97,6 +106,13 @@ signals:
*/
void showAudioToolsChanged(bool show);
/**jsdoc
* @function AvatarInputs.showBubbleToolsChanged
* @param {boolean} show
* @returns {Signal}
*/
void showBubbleToolsChanged(bool show);
/**jsdoc
* @function AvatarInputs.avatarEnteredIgnoreRadius
* @param {QUuid} avatarID
@ -142,6 +158,7 @@ private:
void onAvatarLeftIgnoreRadius();
float _trailingAudioLoudness{ 0 };
bool _showAudioTools { false };
bool _showBubbleTools{ false };
};
#endif // hifi_AvatarInputs_h

View file

@ -180,6 +180,11 @@ void Stats::updateStats(bool force) {
STAT_UPDATE_FLOAT(mbpsIn, nodeList->getInboundKbps() / 1000.0f, 0.01f);
STAT_UPDATE_FLOAT(mbpsOut, nodeList->getOutboundKbps() / 1000.0f, 0.01f);
#ifdef DEBUG_EVENT_QUEUE
STAT_UPDATE(mainThreadQueueDepth, ::hifi::qt::getEventQueueSize(QThread::currentThread()));
STAT_UPDATE(nodeListThreadQueueDepth, ::hifi::qt::getEventQueueSize(nodeList->thread()));
#endif
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer);

View file

@ -14,6 +14,7 @@
#include <OffscreenQmlElement.h>
#include <AudioIOStats.h>
#include <render/Args.h>
#include <shared/QtHelpers.h>
#define STATS_PROPERTY(type, name, initialValue) \
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
@ -179,6 +180,7 @@ private: \
* @property {Vec3} rayPicksUpdated - <em>Read-only.</em>
* @property {Vec3} parabolaPicksUpdated - <em>Read-only.</em>
* @property {Vec3} collisionPicksUpdated - <em>Read-only.</em>
* @property {bool} eventQueueDebuggingOn - <em>Read-only.</em>
*/
// Properties from x onwards are QQuickItem properties.
@ -312,6 +314,14 @@ class Stats : public QQuickItem {
STATS_PROPERTY(QVector3D, parabolaPicksUpdated, QVector3D(0, 0, 0))
STATS_PROPERTY(QVector3D, collisionPicksUpdated, QVector3D(0, 0, 0))
#ifdef DEBUG_EVENT_QUEUE
STATS_PROPERTY(bool, eventQueueDebuggingOn, true)
STATS_PROPERTY(int, mainThreadQueueDepth, -1);
STATS_PROPERTY(int, nodeListThreadQueueDepth, -1);
#else
STATS_PROPERTY(bool, eventQueueDebuggingOn, false)
#endif // DEBUG_EVENT_QUEUE
public:
static Stats* getInstance();
@ -1357,6 +1367,27 @@ signals:
*/
void collisionPicksUpdatedChanged();
/**jsdoc
* Triggered when the value of the <code>eventQueueDebuggingOn</code> property changes.
* @function Stats.eventQueueDebuggingOn
* @returns {Signal}
*/
void eventQueueDebuggingOnChanged();
/**jsdoc
* Triggered when the value of the <code>nodeListThreadQueueDepth</code> property changes.
* @function Stats.nodeListThreadQueueDepth
* @returns {Signal}
*/
void nodeListThreadQueueDepthChanged();
/**jsdoc
* Triggered when the value of the <code>nodeListThreadQueueDepth</code> property changes.
* @function Stats.nodeListThreadQueueDepth
* @returns {Signal}
*/
void mainThreadQueueDepthChanged();
private:
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
bool _resetRecentMaxPacketsSoon{ true };

View file

@ -361,6 +361,12 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
const uint32_t QUAD_STRIDE = 4;
ShapeType type = getShapeType();
auto model = getModel();
if (!model) {
type = SHAPE_TYPE_NONE;
}
if (type == SHAPE_TYPE_COMPOUND) {
updateModelBounds();
@ -442,10 +448,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
// to the visual model and apply them to the collision model (without regard for the
// collision model's extents).
auto model = getModel();
// assert we never fall in here when model not fully loaded
assert(model && model->isLoaded());
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 scaleToFit = dimensions / model->getHFMModel().getUnscaledMeshExtents().size();
// multiply each point by scale before handing the point-set off to the physics engine.
@ -461,7 +463,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
adjustShapeInfoByRegistration(shapeInfo);
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
updateModelBounds();
auto model = getModel();
// assert we never fall in here when model not fully loaded
assert(model && model->isLoaded());
model->updateGeometry();

View file

@ -110,6 +110,10 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
EntityPropertyFlags requestedProperties = propertiesCopy.getChangedProperties();
if (!nodeList->getThisNodeCanGetAndSetPrivateUserData() && requestedProperties.getHasProperty(PROP_PRIVATE_USER_DATA)) {
requestedProperties -= PROP_PRIVATE_USER_DATA;
}
while (encodeResult == OctreeElement::PARTIAL) {
encodeResult = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut, requestedProperties, didntFitProperties);

View file

@ -86,6 +86,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_NAME;
requestedProperties += PROP_LOCKED;
requestedProperties += PROP_USER_DATA;
requestedProperties += PROP_PRIVATE_USER_DATA;
requestedProperties += PROP_HREF;
requestedProperties += PROP_DESCRIPTION;
requestedProperties += PROP_POSITION;
@ -154,7 +155,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
}
OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData) const {
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
const bool destinationNodeCanGetAndSetPrivateUserData) const {
// ALL this fits...
// object ID [16 bytes]
@ -198,6 +200,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID());
}
QString privateUserData = "";
if (destinationNodeCanGetAndSetPrivateUserData) {
privateUserData = getPrivateUserData();
}
EntityPropertyFlags propertiesDidntFit = requestedProperties;
LevelDetails entityLevel = packetData->startLevel();
@ -266,8 +273,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, _simulationOwner.toByteArray());
// convert AVATAR_SELF_ID to actual sessionUUID.
QUuid actualParentID = getParentID();
auto nodeList = DependencyManager::get<NodeList>();
if (actualParentID == AVATAR_SELF_ID) {
auto nodeList = DependencyManager::get<NodeList>();
actualParentID = nodeList->getSessionUUID();
}
APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, actualParentID);
@ -276,6 +283,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_NAME, getName());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData());
APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, privateUserData);
APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
APPEND_ENTITY_PROPERTY(PROP_POSITION, getLocalPosition());
@ -812,6 +820,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_NAME, QString, setName);
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
READ_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, QString, setPrivateUserData);
READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
{ // When we own the simulation we don't accept updates to the entity's transform/velocities
@ -1331,6 +1340,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire
COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(privateUserData, getPrivateUserData);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getLocalPosition);
@ -1479,6 +1489,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(privateUserData, setPrivateUserData);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPosition);
@ -3121,6 +3132,20 @@ void EntityItem::setUserData(const QString& value) {
});
}
QString EntityItem::getPrivateUserData() const {
QString result;
withReadLock([&] {
result = _privateUserData;
});
return result;
}
void EntityItem::setPrivateUserData(const QString& value) {
withWriteLock([&] {
_privateUserData = value;
});
}
// Certifiable Properties
#define DEFINE_PROPERTY_GETTER(type, accessor, var) \
type EntityItem::get##accessor() const { \

View file

@ -134,7 +134,8 @@ public:
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData) const;
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
const bool destinationNodeCanGetAndSetPrivateUserData = false) const;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
@ -332,6 +333,9 @@ public:
QString getUserData() const;
virtual void setUserData(const QString& value); // FIXME: This is suspicious
QString getPrivateUserData() const;
void setPrivateUserData(const QString& value);
// FIXME not thread safe?
const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
void setSimulationOwner(const QUuid& id, uint8_t priority);
@ -644,6 +648,7 @@ protected:
bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC };
bool _locked { ENTITY_ITEM_DEFAULT_LOCKED };
QString _userData { ENTITY_ITEM_DEFAULT_USER_DATA };
QString _privateUserData{ ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA };
SimulationOwner _simulationOwner;
bool _shouldHighlight { false };
QString _name { ENTITY_ITEM_DEFAULT_NAME };

View file

@ -487,6 +487,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_NAME, name);
CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked);
CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData);
CHECK_PROPERTY_CHANGE(PROP_PRIVATE_USER_DATA, privateUserData);
CHECK_PROPERTY_CHANGE(PROP_HREF, href);
CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description);
CHECK_PROPERTY_CHANGE(PROP_POSITION, position);
@ -818,6 +819,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* which you can manipulate the properties of, and use <code>JSON.stringify()</code> to convert the object into a string to
* put in the property.
*
* @property {string} privateUserData="" - Like userData, but only accessible by Entity Server Scripts, AC scripts, and users
* who are given "Can Get and Set Private User Data" permissions from the ACL matrix on the Domain Settings page.
*
* @property {string} script="" - The URL of the client entity script, if any, that is attached to the entity.
* @property {number} scriptTimestamp=0 - Intended to be used to indicate when the client entity script was loaded. Should be
* an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by <code>Date.now()</code>.
@ -1591,6 +1595,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PRIVATE_USER_DATA, privateUserData);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_HREF, href);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DESCRIPTION, description);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POSITION, position);
@ -1999,6 +2004,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName);
COPY_PROPERTY_FROM_QSCRIPTVALUE(locked, bool, setLocked);
COPY_PROPERTY_FROM_QSCRIPTVALUE(userData, QString, setUserData);
COPY_PROPERTY_FROM_QSCRIPTVALUE(privateUserData, QString, setPrivateUserData);
COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref);
COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription);
COPY_PROPERTY_FROM_QSCRIPTVALUE(position, vec3, setPosition);
@ -2288,6 +2294,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(name);
COPY_PROPERTY_IF_CHANGED(locked);
COPY_PROPERTY_IF_CHANGED(userData);
COPY_PROPERTY_IF_CHANGED(privateUserData);
COPY_PROPERTY_IF_CHANGED(href);
COPY_PROPERTY_IF_CHANGED(description);
COPY_PROPERTY_IF_CHANGED(position);
@ -2573,6 +2580,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString);
ADD_PROPERTY_TO_MAP(PROP_LOCKED, Locked, locked, bool);
ADD_PROPERTY_TO_MAP(PROP_USER_DATA, UserData, userData, QString);
ADD_PROPERTY_TO_MAP(PROP_PRIVATE_USER_DATA, PrivateUserData, privateUserData, QString);
ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString);
ADD_PROPERTY_TO_MAP(PROP_DESCRIPTION, Description, description, QString);
ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, vec3);
@ -3047,6 +3055,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData());
APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, properties.getPrivateUserData());
APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref());
APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription());
APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition());
@ -3530,6 +3539,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PRIVATE_USER_DATA, QString, setPrivateUserData);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, vec3, setPosition);
@ -3941,6 +3951,7 @@ void EntityItemProperties::markAllChanged() {
_nameChanged = true;
_lockedChanged = true;
_userDataChanged = true;
_privateUserDataChanged = true;
_hrefChanged = true;
_descriptionChanged = true;
_positionChanged = true;
@ -4303,6 +4314,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (userDataChanged()) {
out += "userData";
}
if (privateUserDataChanged()) {
out += "privateUserData";
}
if (hrefChanged()) {
out += "href";
}

View file

@ -180,6 +180,7 @@ public:
DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME);
DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool, ENTITY_ITEM_DEFAULT_LOCKED);
DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString, ENTITY_ITEM_DEFAULT_USER_DATA);
DEFINE_PROPERTY_REF(PROP_PRIVATE_USER_DATA, PrivateUserData, privateUserData, QString, ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA);
DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, "");
DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, "");
DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3, ENTITY_ITEM_ZERO_VEC3);
@ -607,6 +608,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, PrivateUserData, privateUserData, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulationOwner, simulationOwner, SimulationOwner());
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, "");

View file

@ -28,6 +28,7 @@ const QVector<glm::vec3> ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC = QVector<glm::vec3
const bool ENTITY_ITEM_DEFAULT_LOCKED = false;
const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString("");
const QString ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA = QString("");
const QUuid ENTITY_ITEM_DEFAULT_SIMULATOR_ID = QUuid();
// Certifiable Properties

View file

@ -26,6 +26,7 @@ enum EntityPropertyList {
PROP_NAME,
PROP_LOCKED,
PROP_USER_DATA,
PROP_PRIVATE_USER_DATA,
PROP_HREF,
PROP_DESCRIPTION,
PROP_POSITION,

View file

@ -56,6 +56,7 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership
connect(nodeList.data(), &NodeList::canRezCertifiedChanged, this, &EntityScriptingInterface::canRezCertifiedChanged);
connect(nodeList.data(), &NodeList::canRezTmpCertifiedChanged, this, &EntityScriptingInterface::canRezTmpCertifiedChanged);
connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged);
connect(nodeList.data(), &NodeList::canGetAndSetPrivateUserDataChanged, this, &EntityScriptingInterface::canGetAndSetPrivateUserDataChanged);
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket");
@ -107,6 +108,11 @@ bool EntityScriptingInterface::canReplaceContent() {
return nodeList->getThisNodeCanReplaceContent();
}
bool EntityScriptingInterface::canGetAndSetPrivateUserData() {
auto nodeList = DependencyManager::get<NodeList>();
return nodeList->getThisNodeCanGetAndSetPrivateUserData();
}
void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) {
if (_entityTree) {
disconnect(_entityTree.get(), &EntityTree::addingEntityPointer, this, &EntityScriptingInterface::onAddingEntity);

View file

@ -236,6 +236,14 @@ public slots:
*/
Q_INVOKABLE bool canReplaceContent();
/**jsdoc
* Check whether or not you can get and set private user data.
* @function Entities.canGetAndSetPrivateUserData
* @returns {boolean} <code>true</code> if the domain server will allow the user to get and set private user data,
* otherwise <code>false</code>.
*/
Q_INVOKABLE bool canGetAndSetPrivateUserData();
/**jsdoc
* <p>How an entity is sent over the wire.</p>
* <table>
@ -1861,6 +1869,15 @@ signals:
*/
void canWriteAssetsChanged(bool canWriteAssets);
/**jsdoc
* Triggered when your ability to get and set private user data changes.
* @function Entities.canGetAndSetPrivateUserDataChanged
* @param {boolean} canGetAndSetPrivateUserData - <code>true</code> if you can change the <code>privateUserData</code> property of an entity,
* otherwise <code>false</code>.
* @returns {Signal}
*/
void canGetAndSetPrivateUserDataChanged(bool canGetAndSetPrivateUserData);
/**jsdoc
* Triggered when a mouse button is clicked while the mouse cursor is on an entity, or a controller trigger is fully

View file

@ -1286,6 +1286,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
}
}
if (properties.privateUserDataChanged()) {
int index = changedProperties.indexOf("privateUserData");
if (index >= 0) {
QString changeHint = properties.getPrivateUserData();
changedProperties[index] = QString("privateUserData:") + changeHint;
}
}
if (properties.parentJointIndexChanged()) {
int index = changedProperties.indexOf("parentJointIndex");
if (index >= 0) {
@ -1772,6 +1780,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
bool suppressDisallowedClientScript = false;
bool suppressDisallowedServerScript = false;
bool suppressDisallowedPrivateUserData = false;
bool isPhysics = message.getType() == PacketType::EntityPhysics;
_totalEditMessages++;
@ -1860,7 +1869,22 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
}
}
}
}
if (!properties.getPrivateUserData().isEmpty() && validEditPacket && !senderNode->getCanGetAndSetPrivateUserData()) {
if (wantEditLogging()) {
qCDebug(entities) << "User [" << senderNode->getUUID()
<< "] is attempting to set private user data but user isn't allowed; edit rejected...";
}
// If this was an add, we also want to tell the client that sent this edit that the entity was not added.
if (isAdd) {
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
validEditPacket = false;
} else {
suppressDisallowedPrivateUserData = true;
}
}
if (!isClone) {
@ -1915,6 +1939,11 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
properties.setServerScripts(existingEntity->getServerScripts());
}
if (suppressDisallowedPrivateUserData) {
bumpTimestamp(properties);
properties.setPrivateUserData(existingEntity->getPrivateUserData());
}
// if the EntityItem exists, then update it
startLogging = usecTimestampNow();
if (wantEditLogging()) {

View file

@ -294,9 +294,7 @@ void ModelEntityItem::setModelURL(const QString& url) {
withWriteLock([&] {
if (_modelURL != url) {
_modelURL = url;
if (_shapeType == SHAPE_TYPE_STATIC_MESH) {
_flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
}
_flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
}
});
}
@ -329,11 +327,8 @@ const Transform ModelEntityItem::getTransform(bool& success, int depth) const {
void ModelEntityItem::setCompoundShapeURL(const QString& url) {
withWriteLock([&] {
if (_compoundShapeURL.get() != url) {
ShapeType oldType = computeTrueShapeType();
_compoundShapeURL.set(url);
if (oldType != computeTrueShapeType()) {
_flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
}
_flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
}
});
}

View file

@ -567,9 +567,19 @@ bool DomainHandler::checkInPacketTimeout() {
}
if (_checkInPacketsSinceLastReply > MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
auto nodeList = DependencyManager::get<NodeList>();
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
// so emit our signal that says that
qCDebug(networking_ice) << "Limit of silent domain checkins reached";
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(nodeList->thread());
qCDebug(networking) << "Limit of silent domain checkins reached (network qt queue: " << nodeListQueueSize << ")";
#else // DEBUG_EVENT_QUEUE
qCDebug(networking) << "Limit of silent domain checkins reached";
#endif // DEBUG_EVENT_QUEUE
emit limitOfSilentDomainCheckInsReached();
return true;
} else {

View file

@ -197,6 +197,10 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) {
newPermissions.can(NodePermissions::Permission::canReplaceDomainContent)) {
emit canReplaceContentChanged(_permissions.can(NodePermissions::Permission::canReplaceDomainContent));
}
if (originalPermissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData) !=
newPermissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData)) {
emit canGetAndSetPrivateUserDataChanged(_permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData));
}
}
void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) {

View file

@ -124,6 +124,7 @@ public:
bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); }
bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
bool getThisNodeCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); }
bool getThisNodeCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); }
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort);
@ -368,6 +369,7 @@ signals:
void canWriteAssetsChanged(bool canWriteAssets);
void canKickChanged(bool canKick);
void canReplaceContentChanged(bool canReplaceContent);
void canGetAndSetPrivateUserDataChanged(bool canGetAndSetPrivateUserData);
protected slots:
void connectedForLocalSocketTest();

View file

@ -83,6 +83,7 @@ public:
bool getCanWriteToAssetServer() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); }
bool getCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
bool getCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); }
bool getCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); }
using NodesIgnoredPair = std::pair<std::vector<QUuid>, bool>;

View file

@ -67,6 +67,7 @@ NodePermissions::NodePermissions(QMap<QString, QVariant> perms) {
Permission::canConnectPastMaxCapacity : Permission::none;
permissions |= perms["id_can_kick"].toBool() ? Permission::canKick : Permission::none;
permissions |= perms["id_can_replace_content"].toBool() ? Permission::canReplaceDomainContent : Permission::none;
permissions |= perms["id_can_get_and_set_private_user_data"].toBool() ? Permission::canGetAndSetPrivateUserData : Permission::none;
}
QVariant NodePermissions::toVariant(QHash<QUuid, GroupRank> groupRanks) {
@ -94,6 +95,7 @@ QVariant NodePermissions::toVariant(QHash<QUuid, GroupRank> groupRanks) {
values["id_can_connect_past_max_capacity"] = can(Permission::canConnectPastMaxCapacity);
values["id_can_kick"] = can(Permission::canKick);
values["id_can_replace_content"] = can(Permission::canReplaceDomainContent);
values["id_can_get_and_set_private_user_data"] = can(Permission::canGetAndSetPrivateUserData);
return QVariant(values);
}
@ -166,6 +168,9 @@ QDebug operator<<(QDebug debug, const NodePermissions& perms) {
if (perms.can(NodePermissions::Permission::canReplaceDomainContent)) {
debug << " can_replace_content";
}
if (perms.can(NodePermissions::Permission::canGetAndSetPrivateUserData)) {
debug << " get-and-set-private-user-data";
}
debug.nospace() << "]";
return debug.nospace();
}

View file

@ -75,7 +75,8 @@ public:
canKick = 64,
canReplaceDomainContent = 128,
canRezPermanentCertifiedEntities = 256,
canRezTemporaryCertifiedEntities = 512
canRezTemporaryCertifiedEntities = 512,
canGetAndSetPrivateUserData = 1024
};
Q_DECLARE_FLAGS(Permissions, Permission)
Permissions permissions;

View file

@ -18,6 +18,7 @@
#include <QtCore/QTimer>
#include <LogHandler.h>
#include <shared/QtHelpers.h>
#include "NetworkLogging.h"
@ -94,6 +95,10 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy
void ThreadedAssignment::addPacketStatsAndSendStatsPacket(QJsonObject statsObject) {
auto nodeList = DependencyManager::get<NodeList>();
#ifdef DEBUG_EVENT_QUEUE
statsObject["nodelist_event_queue_size"] = ::hifi::qt::getEventQueueSize(nodeList->thread());
#endif
QJsonObject ioStats;
ioStats["inbound_kbps"] = nodeList->getInboundKbps();
ioStats["inbound_pps"] = nodeList->getInboundPPS();

View file

@ -270,6 +270,7 @@ enum class EntityVersion : PacketVersion {
DisableWebMedia,
ParticleShapeType,
ParticleShapeTypeDeadlockFix,
PrivateUserData,
// Add new versions above here
NUM_PACKET_TYPE,

View file

@ -249,12 +249,19 @@ void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() {
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShapeByKey(requestItr->shapeHash));
if (shape) {
// shape is ready at last!
// But the entity's desired shape might have changed since last requested
// --> rebuild the ShapeInfo to verify hash
// but the entity's physics desired physics status may have changed since last requested
if (!entity->shouldBePhysical()) {
requestItr = _shapeRequests.erase(requestItr);
continue;
}
// rebuild the ShapeInfo to verify hash because entity's desired shape may have changed
// TODO? is there a better way to do this?
ShapeInfo shapeInfo;
entity->computeShapeInfo(shapeInfo);
if (shapeInfo.getHash() != requestItr->shapeHash) {
if (shapeInfo.getType() == SHAPE_TYPE_NONE) {
requestItr = _shapeRequests.erase(requestItr);
} else if (shapeInfo.getHash() != requestItr->shapeHash) {
// bummer, the hashes are different and we no longer want the shape we've received
ObjectMotionState::getShapeManager()->releaseShape(shape);
// try again

View file

@ -49,11 +49,15 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
const btCollisionShape* shape = nullptr;
if (info.getType() == SHAPE_TYPE_STATIC_MESH) {
uint64_t hash = info.getHash();
// bump the request count to the caller knows we're
// starting or waiting on a thread.
++_workRequestCount;
const auto itr = std::find(_pendingMeshShapes.begin(), _pendingMeshShapes.end(), hash);
if (itr == _pendingMeshShapes.end()) {
// start a worker
_pendingMeshShapes.push_back(hash);
++_workRequestCount;
// try to recycle old deadWorker
ShapeFactory::Worker* worker = _deadWorker;
if (!worker) {

View file

@ -1,5 +1,7 @@
set(TARGET_NAME shared)
include_directories("${QT_DIR}/include/QtCore/${QT_VERSION}/QtCore" "${QT_DIR}/include/QtCore/${QT_VERSION}")
# TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp)
setup_hifi_library(Gui Network Script)

View file

@ -13,6 +13,17 @@
#include <QtCore/QLoggingCategory>
#include <QtCore/QReadWriteLock>
// Inspecting of the qt event queue depth
// requres access to private Qt datastructures
// defined in private Qt headers
#ifdef DEBUG_EVENT_QUEUE
#include "../TryLocker.h"
#define QT_BOOTSTRAPPED
#include <private/qthread_p.h>
#include <private/qobject_p.h>
#undef QT_BOOTSTRAPPED
#endif // DEBUG_EVENT_QUEUE
#include "../Profile.h"
Q_LOGGING_CATEGORY(thread_safety, "hifi.thread_safety")
@ -80,6 +91,35 @@ bool blockingInvokeMethod(
return blockingInvokeMethod(function, obj, member, QGenericReturnArgument(), val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
// Inspecting of the qt event queue
// requres access to private Qt datastructures
// Querying the event queue should be done with
// care as it could lock the threadData->postEventList.mutex
// The code uses a tryLock to avoid the possability of a
// deadlock during a call to this code, although that is unlikely
//
#ifdef DEBUG_EVENT_QUEUE
int getEventQueueSize(QThread* thread) {
auto threadData = QThreadData::get2(thread);
{
MutexTryLocker locker(threadData->postEventList.mutex);
if (locker.isLocked()) {
return threadData->postEventList.size();
}
}
return -1;
}
void dumpEventQueue(QThread* thread) {
auto threadData = QThreadData::get2(thread);
QMutexLocker locker(&threadData->postEventList.mutex);
qDebug() << "Event list, size =" << threadData->postEventList.size();
for (auto& postEvent : threadData->postEventList) {
QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None);
qDebug() << " " << type;
}
}
#endif // DEBUG_EVENT_QUEUE
} }

View file

@ -12,6 +12,10 @@
#include <QtCore/QObject>
#ifdef WIN32
// Enable event queue debugging
#define DEBUG_EVENT_QUEUE
#endif // WIN32
namespace hifi { namespace qt {
void addBlockingForbiddenThread(const QString& name, QThread* thread = nullptr);
@ -45,6 +49,18 @@ bool blockingInvokeMethod(
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument());
// Inspecting of the qt event queue
// requres access to private Qt datastructures
// Querying the event queue should be done with
// care as it could lock the threadData->postEventList.mutex
// The code uses a tryLock to avoid the possability of a
// deadlock during a call to this code, although that is unlikely
// When getEventQueueSize fails to get the lock, it returns -1
#ifdef DEBUG_EVENT_QUEUE
int getEventQueueSize(QThread* thread);
void dumpEventQueue(QThread* thread);
#endif // DEBUG_EVENT_QUEUE
} }
#define BLOCKING_INVOKE_METHOD(obj, member, ...) \

View file

@ -5,8 +5,7 @@ var toolBar = (function() {
toolBar,
activeButton,
newModelButton,
newCubeButton,
newSphereButton,
newShapeButton,
newLightButton,
newTextButton,
newWebButton,
@ -41,20 +40,13 @@ var toolBar = (function() {
visible: false
});
newCubeButton = toolBar.addButton({
objectName: "newCubeButton",
newShapeButton = toolBar.addButton({
objectName: "newShapeButton",
imageURL: toolIconUrl + "cube-01.svg",
alpha: 0.9,
visible: false
});
newSphereButton = toolBar.addButton({
objectName: "newSphereButton",
imageURL: toolIconUrl + "sphere-01.svg",
alpha: 0.9,
visible: false
});
newLightButton = toolBar.addButton({
objectName: "newLightButton",
imageURL: toolIconUrl + "light-01.svg",
@ -111,8 +103,7 @@ var toolBar = (function() {
// Sets visibility of tool buttons, excluding the power button
that.showTools = function(doShow) {
newModelButton.writeProperty('visible', doShow);
newCubeButton.writeProperty('visible', doShow);
newSphereButton.writeProperty('visible', doShow);
newShapeButton.writeProperty('visible', doShow);
newLightButton.writeProperty('visible', doShow);
newTextButton.writeProperty('visible', doShow);
newWebButton.writeProperty('visible', doShow);

View file

@ -860,15 +860,10 @@ var toolBar = (function () {
addButton("newModelButton", createNewEntityDialogButtonCallback("Model"));
addButton("newCubeButton", function () {
addButton("newShapeButton", function () {
createNewEntity({
type: "Box",
});
});
addButton("newSphereButton", function () {
createNewEntity({
type: "Sphere",
type: "Shape",
shape: "Cube",
});
});

View file

@ -0,0 +1,26 @@
//
// Simplified Nametag
// defaultLocalEntityProps.js
// Created by Milad Nazeri on 2019-03-09
// Copyright 2019 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
//
// Base properties for the Local Entities
//
var localEntityProps = {
dimensions: [1, 0.1, 0],
type: "Text",
lineHeight: 0.1,
textColor: "#ffffff",
textAlpha: 1.0,
backgroundColor: "#2d2d2d",
backgroundAlpha: 0.6,
billboardMode: "full",
lifetime: 3,
canCastShadow: true
};
module.exports = localEntityProps;

View file

@ -0,0 +1,154 @@
//
// Simplified Nametag
// entityMaker.js
// Created by Milad Nazeri on 2019-02-19
// Copyright 2019 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
//
// A helper library to make entities
//
Script.require('./objectAssign.js');
function EntityMaker(type) {
this.properties = {};
this.cache = {};
this.id = null;
this.created = null;
this.type = type;
}
// *************************************
// START API
// *************************************
// #region API
// Add properties to the cache / temporary storage
function add(props){
// You can either add an object of props or 2 arguments as key and value
if (arguments.length === 2) {
var property = arguments[0];
var value = arguments[1];
props = {};
props[property] = value;
}
this.properties = Object.assign({}, this.properties, props);
this.cache = Object.assign({}, this.cache, this.properties);
return this;
}
// Sends the current temporary stroage to edit the entity
function sync(){
Entities.editEntity(this.id, this.properties);
this.properties = {};
return this;
}
// Immediately edit the entity with the properties given
function edit(props){
if (arguments.length === 2) {
var property = arguments[0];
var value = arguments[1];
props = {};
props[property] = value;
}
this.properties = Object.assign({}, this.properties, props);
this.cache = Object.assign({}, this.cache, this.properties);
this.sync();
return this;
}
// Get a property either from the cache or by querying the entity directly
function get(propertyKeys, queryEntity){
if (queryEntity && typeof propertyKeys === 'string') {
var propertyValue = Entities.getEntityProperties(this.id, propertyKeys)[propertyKeys];
this.properties[propertyKeys] = propertyValue;
this.cache = Object.assign({}, this.cache, this.properties);
return propertyValue;
}
if (queryEntity && Array.isArray(propertyKeys)) {
var entityProps = Entities.getEntityProperties(this.id, propertyKeys);
for (var prop in entityProps) {
if (propertyKeys.indexOf(prop) === -1) {
delete entityProps[prop];
} else {
this.properties[prop] = entityProps[prop];
}
}
return entityProps;
}
if (Array.isArray(propertyKeys)) {
var recombinedProps = {};
propertyKeys.forEach(function (prop) {
recombinedProps[prop] = this.cache[prop];
}, this);
return recombinedProps;
}
return this.cache[propertyKeys];
}
// Show the entity
function show(){
this.edit({ visible: true });
return this;
}
// Hide the enity
function hide(){
this.edit({ visible: false });
}
// Add an entity if it isn't created
function create(clearPropertiesAfter){
this.id = Entities.addEntity(this.properties, this.type);
if (clearPropertiesAfter) {
this.properties = {};
}
return this;
}
// Delete the entity
function destroy(){
Entities.deleteEntity(this.id);
return this;
}
// #endregion
// *************************************
// END API
// *************************************
EntityMaker.prototype = {
add: add,
sync: sync,
edit: edit,
get: get,
show: show,
hide: hide,
create: create,
destroy: destroy
};
module.exports = EntityMaker;

View file

@ -0,0 +1,669 @@
//
// Simplified Nametag
// nameTagListManager.js
// Created by Milad Nazeri on 2019-03-09
// Copyright 2019 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
//
// Helps manage the list of avatars added to the nametag list
//
var ON = 'ON';
var OFF = 'OFF';
var DEBUG_ON = true;
var DEBUG_OFF = false;
var log = Script.require(
'https://hifi-content.s3.amazonaws.com/milad/ROLC/d/ROLC_High-Fidelity/02_Organize/O_Projects/Repos/hifi-content/developerTools/sharedLibraries/easyLog/easyLog.js?'
+ Date.now())(DEBUG_OFF, 'nameTagListManager.js');
var EntityMaker = Script.require('./entityMaker.js?' + Date.now());
var entityProps = Script.require('./defaultLocalEntityProps.js?' + Date.now());
var textHelper = new (Script.require('./textHelper.js?' + Date.now()));
var X = 0;
var Y = 1;
var Z = 2;
var HALF = 0.5;
var CLEAR_ENTITY_EDIT_PROPS = true;
var MILISECONDS_IN_SECOND = 1000;
// *************************************
// START UTILTY
// *************************************
// #region UTILTY
// Properties to give new avatars added to the list
function NewAvatarProps() {
return {
avatarInfo: null,
previousDistance: null,
currentDistance: null,
initialDistance: null,
initialDimensions: null,
previousName: null,
timeoutStarted: false
};
}
// Makes sure clear interval exists before changing
function maybeClearInterval() {
if (_this.redrawTimeout) {
Script.clearInterval(_this.redrawTimeout);
_this.redrawTimeout = null;
}
}
// Calculate our initial properties for the nametag
var Z_SIZE = 0.01;
var LINE_HEIGHT_SCALER = 0.99;
var DISTANCE_SCALER_ON = 0.35;
var DISTANCE_SCALER_ALWAYS_ON = 0.65;
var distanceScaler = DISTANCE_SCALER_ON;
var userScaler = 1.0;
var DEFAULT_LINE_HEIGHT = entityProps.lineHeight;
function calculateInitialProperties(uuid) {
var avatar = _this.avatars[uuid];
var avatarInfo = avatar.avatarInfo;
var adjustedScaler = null;
var distance = null;
var dimensions = null;
var lineHeight = null;
var scaledDimensions = null;
var name = null;
// Handle if we are asking for the main or sub properties
name = avatarInfo.displayName;
// Use the text helper to calculate what our dimensions for the text box should be
textHelper
.setText(name)
.setLineHeight(DEFAULT_LINE_HEIGHT);
// Calculate the distance from the camera to the target avatar
distance = getDistance(uuid);
// Adjust the distance by the distance scaler
distanceScaler = avatarNametagMode === "on" ? DISTANCE_SCALER_ON : DISTANCE_SCALER_ALWAYS_ON;
adjustedScaler = distance * distanceScaler;
// Get the new dimensions from the text helper
dimensions = [textHelper.getTotalTextLength(), DEFAULT_LINE_HEIGHT, Z_SIZE];
// Adjust the dimensions by the modified distance scaler
scaledDimensions = Vec3.multiply(dimensions, adjustedScaler);
// Adjust the lineheight to be the new scaled dimensions Y
lineHeight = scaledDimensions[Y] * LINE_HEIGHT_SCALER;
return {
distance: distance,
scaledDimensions: scaledDimensions,
lineHeight: lineHeight
};
}
// Used in alwaysOn mode to show or hide if they reached the max radius
function showHide(uuid, type) {
var avatar = _this.avatars[uuid];
var nametag = avatar.nametag;
if (type === "show") {
nametag.show();
} else {
nametag.hide();
}
}
// Go through the selected avatar list and see if any of the avatars need a redraw
function checkAllSelectedForRedraw() {
for (var avatar in _this.selectedAvatars) {
maybeRedraw(avatar);
}
}
// Remake the nametags if the display name changes
function updateName(uuid) {
var avatar = _this.avatars[uuid];
avatar.nametag.destroy();
avatar.nametag = new EntityMaker('local').add(entityProps);
makeNameTag(uuid);
}
// Get the current data for an avatar.
function getAvatarData(uuid) {
var avatar = _this.avatars[uuid];
var avatarInfo = avatar.avatarInfo;
var newAvatarInfo = AvatarManager.getAvatar(uuid);
// Save the username so it doesn't get overwritten when grabbing new avatarData
var combinedAvatarInfo = Object.assign({}, newAvatarInfo, {
username: avatarInfo === null ? null : avatarInfo.username
});
// Now combine that avatar data with the main avatar object
_this.avatars[uuid] = Object.assign({}, avatar, { avatarInfo: combinedAvatarInfo });
return _this;
}
// Calculate the distance between the camera and the target avatar
function getDistance(uuid, checkAvatar, shouldSave) {
checkAvatar = checkAvatar || false;
shouldSave = shouldSave || true;
var eye = checkAvatar ? MyAvatar.position : Camera.position;
var avatar = _this.avatars[uuid];
var avatarInfo = avatar.avatarInfo;
var target = avatarInfo.position;
var currentDistance = Vec3.distance(target, eye);
if (!checkAvatar && shouldSave) {
avatar.previousDistance = avatar.currentDistance;
avatar.currentDistance = currentDistance;
}
return currentDistance;
}
// Check to see if we need to toggle our interval check because we went to 0 avatars
// or if we got our first avatar in the select list
function shouldToggleInterval() {
var currentNumberOfAvatarsSelected = Object.keys(_this.selectedAvatars).length;
if (currentNumberOfAvatarsSelected === 0 && _this.redrawTimeout) {
toggleInterval();
return;
}
if (currentNumberOfAvatarsSelected > 0 && !_this.redrawTimeout) {
toggleInterval();
return;
}
}
// Turn off and on the redraw check
var INTERVAL_CHECK_MS = 30;
function toggleInterval() {
if (_this.redrawTimeout) {
maybeClearInterval();
} else {
_this.redrawTimeout =
Script.setInterval(checkAllSelectedForRedraw, INTERVAL_CHECK_MS);
}
}
// handle turning the peristenet mode on
function handleAlwaysOnMode(shouldTurnOnAlwaysOnMode) {
_this.reset();
if (shouldTurnOnAlwaysOnMode) {
AvatarManager
.getAvatarIdentifiers()
.forEach(function (avatar) {
if (avatar) {
add(avatar);
}
});
}
}
// #endregion
// *************************************
// END UTILTY
// *************************************
// *************************************
// START Nametag
// *************************************
// #region Nametag
var _this = null;
function nameTagListManager() {
_this = this;
_this.avatars = {};
_this.selectedAvatars = {};
_this.redrawTimeout = null;
}
// Create or make visible either the sub or the main tag.
var REDRAW_TIMEOUT_AMOUNT_MS = 500;
var LEFT_MARGIN_SCALER = 0.15;
var RIGHT_MARGIN_SCALER = 0.10;
var TOP_MARGIN_SCALER = 0.07;
var BOTTOM_MARGIN_SCALER = 0.03;
var ABOVE_HEAD_OFFSET = 0.30;
var DISTANCE_SCALER_INTERPOLATION_OFFSET_ALWAYSON = 25;
var DISTANCE_SCALER_INTERPOLATION_OFFSET_ON = 10;
var maxDistance = MAX_RADIUS_IGNORE_METERS;
var onModeScalar = 0.60;
var alwaysOnModeScalar = -0.55;
function makeNameTag(uuid) {
var avatar = _this.avatars[uuid];
var avatarInfo = avatar.avatarInfo;
var nametag = avatar.nametag;
// Make sure an anonymous name is covered before sending to calculate
avatarInfo.displayName = avatarInfo.displayName === "" ? "anonymous" : avatarInfo.displayName.trim();
avatar.previousName = avatarInfo.displayName;
// Returns back the properties we need based on what we are looking for and the distance from the avatar
var calculatedProps = calculateInitialProperties(uuid);
var distance = calculatedProps.distance;
var scaledDimensions = calculatedProps.scaledDimensions;
var lineHeight = calculatedProps.lineHeight;
// Capture the inital dimensions, distance, and displayName in case we need to redraw
avatar.previousDisplayName = avatarInfo.displayName;
avatar.initialDimensions = scaledDimensions;
avatar.initialDistance = distance;
var name = avatarInfo.displayName;
var parentID = uuid;
nametag.add("text", name);
// Multiply the new dimensions and line height with the user selected scaler
scaledDimensions = Vec3.multiply(scaledDimensions, userScaler);
maxDistance = avatarNametagMode === "on"
? MAX_ON_MODE_DISTANCE + DISTANCE_SCALER_INTERPOLATION_OFFSET_ON
: MAX_RADIUS_IGNORE_METERS + DISTANCE_SCALER_INTERPOLATION_OFFSET_ALWAYSON;
var finalScaler = (distance - maxDistance) / (MIN_DISTANCE - maxDistance);
var remainder = 1 - finalScaler;
var multipliedRemainderOn = remainder * onModeScalar;
var multipliedRemainderAlwaysOn = remainder * alwaysOnModeScalar;
finalScaler = avatarNametagMode === "on" ? finalScaler + multipliedRemainderOn : finalScaler + multipliedRemainderAlwaysOn;
scaledDimensions = Vec3.multiply(scaledDimensions, finalScaler);
lineHeight = scaledDimensions[Y] * LINE_HEIGHT_SCALER;
// Add some room for the margin by using lineHeight as a reference
scaledDimensions[X] += (lineHeight * LEFT_MARGIN_SCALER) + (lineHeight * RIGHT_MARGIN_SCALER);
scaledDimensions[Y] += (lineHeight * TOP_MARGIN_SCALER) + (lineHeight * BOTTOM_MARGIN_SCALER);
var scaledDimenionsYHalf = scaledDimensions[Y] * HALF;
var AvatarData = AvatarManager.getAvatar(uuid);
var headJointIndex = AvatarData.getJointIndex("Head");
var jointInObjectFrame = AvatarData.getAbsoluteJointTranslationInObjectFrame(headJointIndex);
var nameTagPosition = jointInObjectFrame.y + scaledDimenionsYHalf + ABOVE_HEAD_OFFSET;
var localPosition = [0, nameTagPosition, 0];
var visible = true;
if (avatarNametagMode === "alwaysOn") {
var currentDistance = getDistance(uuid, CHECK_AVATAR, false);
visible = currentDistance > MAX_RADIUS_IGNORE_METERS ? false : true;
}
nametag
.add("leftMargin", lineHeight * LEFT_MARGIN_SCALER)
.add("rightMargin", lineHeight * RIGHT_MARGIN_SCALER)
.add("topMargin", lineHeight * TOP_MARGIN_SCALER)
.add("bottomMargin", lineHeight * BOTTOM_MARGIN_SCALER)
.add("lineHeight", lineHeight)
.add("dimensions", scaledDimensions)
.add("parentID", parentID)
.add("localPosition", localPosition)
.add("visible", visible)
.create(CLEAR_ENTITY_EDIT_PROPS);
Script.setTimeout(function () {
nametag.edit("visible", true);
}, REDRAW_TIMEOUT_AMOUNT_MS);
}
// Check to see if the display named changed or if the distance is big enough to need a redraw.
var MAX_RADIUS_IGNORE_METERS = 22;
var MAX_ON_MODE_DISTANCE = 30;
var CHECK_AVATAR = true;
var MIN_DISTANCE = 0.2;
function maybeRedraw(uuid) {
var avatar = _this.avatars[uuid];
var avatarInfo = avatar.avatarInfo;
getAvatarData(uuid);
getDistance(uuid);
var avatarDistance = getDistance(uuid, CHECK_AVATAR, false);
if (avatarNametagMode === "alwaysOn" && avatarDistance > MAX_RADIUS_IGNORE_METERS) {
showHide(uuid, "hide");
}
if (avatarNametagMode === "alwaysOn" && avatarDistance < MAX_RADIUS_IGNORE_METERS) {
showHide(uuid, "show");
}
avatarInfo.displayName = avatarInfo.displayName === "" ? "anonymous" : avatarInfo.displayName.trim();
if (avatar.previousName !== avatarInfo.displayName) {
updateName(uuid, avatarInfo.displayName);
} else {
redraw(uuid);
}
}
// Handle redrawing if needed
function redraw(uuid) {
var avatar = _this.avatars[uuid];
var nametag = avatar.nametag;
var initialDimensions = null;
var initialDistance = null;
var currentDistance = null;
var newDimensions = null;
var lineHeight = null;
initialDistance = avatar.initialDistance;
currentDistance = avatar.currentDistance;
initialDimensions = avatar.initialDimensions;
// Find our new dimensions from the new distance
newDimensions = [
(initialDimensions[X] / initialDistance) * currentDistance,
(initialDimensions[Y] / initialDistance) * currentDistance,
(initialDimensions[Z] / initialDistance) * currentDistance
];
// Multiply the new dimensions and line height with the user selected scaler
newDimensions = Vec3.multiply(newDimensions, userScaler);
var distance = getDistance(uuid, false, false);
maxDistance = avatarNametagMode === "on"
? MAX_ON_MODE_DISTANCE + DISTANCE_SCALER_INTERPOLATION_OFFSET_ON
: MAX_RADIUS_IGNORE_METERS + DISTANCE_SCALER_INTERPOLATION_OFFSET_ALWAYSON;
var finalScaler = (distance - maxDistance) / (MIN_DISTANCE - maxDistance);
var remainder = 1 - finalScaler;
var multipliedRemainderOn = remainder * onModeScalar;
var multipliedRemainderAlwaysOn = remainder * alwaysOnModeScalar;
finalScaler = avatarNametagMode === "on" ? finalScaler + multipliedRemainderOn : finalScaler + multipliedRemainderAlwaysOn;
newDimensions = Vec3.multiply(newDimensions, finalScaler);
lineHeight = newDimensions[Y] * LINE_HEIGHT_SCALER;
// Add some room for the margin by using lineHeight as a reference
newDimensions[X] += (lineHeight * LEFT_MARGIN_SCALER) + (lineHeight * RIGHT_MARGIN_SCALER);
newDimensions[Y] += (lineHeight * TOP_MARGIN_SCALER) + (lineHeight * BOTTOM_MARGIN_SCALER);
// We can generalize some of the processes that are similar in makeNameTag() and redraw() if we wanted to reduce some code
var newDimenionsYHalf = newDimensions[Y] * HALF;
var AvatarData = AvatarManager.getAvatar(uuid);
var headJointIndex = AvatarData.getJointIndex("Head");
var jointInObjectFrame = AvatarData.getAbsoluteJointTranslationInObjectFrame(headJointIndex);
var nameTagPosition = jointInObjectFrame.y + newDimenionsYHalf + ABOVE_HEAD_OFFSET;
var localPosition = [0, nameTagPosition, 0];
nametag
.add("leftMargin", lineHeight * LEFT_MARGIN_SCALER)
.add("rightMargin", lineHeight * RIGHT_MARGIN_SCALER)
.add("topMargin", lineHeight * TOP_MARGIN_SCALER)
.add("bottomMargin", lineHeight * BOTTOM_MARGIN_SCALER)
.add("lineHeight", lineHeight)
.add("dimensions", newDimensions)
.add("localPosition", localPosition)
.sync();
}
// Add a user to the list.
var DEFAULT_LIFETIME = entityProps.lifetime;
// add a user to our current selections
function add(uuid) {
// User Doesn't exist so give them new props and save in the cache, and get their current avatar info.
if (!_this.avatars[uuid]) {
_this.avatars[uuid] = new NewAvatarProps();
getAvatarData(uuid);
}
var avatar = _this.avatars[uuid];
_this.selectedAvatars[uuid] = true;
if (avatarNametagMode === "alwaysOn") {
entityProps.lifetime = -1;
} else {
entityProps.lifetime = DEFAULT_LIFETIME;
}
avatar.nametag = new EntityMaker('local').add(entityProps);
// When the user clicks someone, we create their nametag
makeNameTag(uuid);
var deleteEnttyInMiliseconds = entityProps.lifetime * MILISECONDS_IN_SECOND;
// Remove from list after lifetime is over
if (avatarNametagMode === "on") {
avatar.timeoutStarted = Script.setTimeout(function () {
removeNametag(uuid);
}, deleteEnttyInMiliseconds);
}
// Check to see if anyone is in the selected list now to see if we need to start the interval checking
shouldToggleInterval();
return _this;
}
// Remove the avatar from the list.
function remove(uuid) {
if (_this.selectedAvatars[uuid]) {
delete _this.selectedAvatars[uuid];
}
removeNametag(uuid);
shouldToggleInterval();
delete _this.avatars[uuid];
return _this;
}
// Remove all the current nametags.
function removeAllNametags() {
for (var uuid in _this.selectedAvatars) {
removeNametag(uuid);
}
return _this;
}
// Remove a single Nametag.
function removeNametag(uuid) {
var avatar = _this.avatars[uuid];
if (avatar) {
avatar.nametag.destroy();
delete _this.selectedAvatars[uuid];
return _this;
}
}
// #endregion
// *************************************
// END Nametag
// *************************************
// *************************************
// START API
// *************************************
// #region API
// Create the manager and hook up username signal
function create() {
return _this;
}
// Destory the manager and disconnect from username signal
function destroy() {
_this.reset();
return _this;
}
// Check to see if we need to delete any close by nametags
var MAX_DELETE_RANGE = 4;
function checkIfAnyAreClose(target) {
var targetPosition = AvatarManager.getAvatar(target).position;
for (var uuid in _this.selectedAvatars) {
var position = AvatarManager.getAvatar(uuid).position;
var distance = Vec3.distance(position, targetPosition);
if (distance <= MAX_DELETE_RANGE) {
var timeoutStarted = _this.avatars[uuid].timeoutStarted;
if (timeoutStarted) {
Script.clearTimeout(timeoutStarted);
timeoutStarted = null;
}
removeNametag(uuid);
}
}
}
// Handles what happens when an avatar gets triggered on
function handleSelect(uuid) {
if (avatarNametagMode === "off" || avatarNametagMode === "alwaysOn") {
return;
}
var inSelected = uuid in _this.selectedAvatars;
if (inSelected) {
if (avatarNametagMode === "on") {
var timeoutStarted = _this.avatars[uuid].timeoutStarted;
if (timeoutStarted) {
Script.clearTimeout(timeoutStarted);
timeoutStarted = null;
}
}
removeNametag(uuid);
} else {
checkIfAnyAreClose(uuid);
add(uuid);
}
}
// Check to see if we need to clear timeouts for avatars
function maybeClearAllTimeouts() {
for (var uuid in _this.selectedAvatars) {
var timeoutStarted = _this.avatars[uuid].timeoutStarted;
if (timeoutStarted) {
Script.clearTimeout(timeoutStarted);
timeoutStarted = null;
}
}
}
// Check to see if the uuid is in the avatars list before removing
function maybeRemove(uuid) {
if (uuid in _this.avatars) {
remove(uuid);
}
}
// Check to see if we need to add this user to our list
function maybeAdd(uuid) {
if (uuid && avatarNametagMode === "alwaysOn" && !(uuid in _this.avatars)) {
add(uuid);
}
}
// Register the beggining scaler in case it was saved from a previous session
function registerInitialScaler(initalScaler) {
userScaler = initalScaler;
}
// Handle the user updating scale
function updateUserScaler(newUSerScaler) {
userScaler = newUSerScaler;
for (var avatar in _this.selectedAvatars) {
redraw(avatar);
}
}
// Reset the avatar list
function reset() {
maybeClearAllTimeouts();
removeAllNametags();
_this.avatars = {};
shouldToggleInterval();
return _this;
}
// Update the nametag display mode
var avatarNametagMode = "on";
function handleAvatarNametagMode(newAvatarNametagMode) {
if (avatarNametagMode === "alwaysOn") {
handleAlwaysOnMode(false);
}
avatarNametagMode = newAvatarNametagMode;
if (avatarNametagMode === "alwaysOn") {
handleAlwaysOnMode(true);
}
if (avatarNametagMode === "off" || avatarNametagMode === "on") {
reset();
}
}
// #endregion
// *************************************
// END API
// *************************************
nameTagListManager.prototype = {
create: create,
destroy: destroy,
handleSelect: handleSelect,
maybeRemove: maybeRemove,
maybeAdd: maybeAdd,
registerInitialScaler: registerInitialScaler,
updateUserScaler: updateUserScaler,
handleAvatarNametagMode: handleAvatarNametagMode,
reset: reset
};
module.exports = nameTagListManager;

View file

@ -0,0 +1,32 @@
// taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
if (typeof Object.assign != 'function') {
// Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}

View file

@ -0,0 +1,305 @@
//
// Simplified Nametag
// pickRayController.js
// Created by Milad Nazeri on 2019-03-08
// Copyright 2019 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
//
// Easy pickray controllers for Entities, Overlays, and Avatars
//
var _this;
function PickRayController(){
_this = this;
_this.rayType = null;
_this.eventHandler = null;
_this.intersection = null;
_this.lastPick = null;
_this.currentPick = null;
_this.mappingName = null;
_this.mapping = null;
_this._boundMousePressHandler = null;
_this.shouldDoublePress = null;
_this.controllEnabled = false;
}
// *************************************
// START UTILITY
// *************************************
// #region UTILITY
// Returns the right UUID based on hand triggered
function getUUIDFromLaser(hand) {
hand = hand === Controller.Standard.LeftHand
? Controller.Standard.LeftHand
: Controller.Standard.RightHand;
var pose = getControllerWorldLocation(hand);
var start = pose.position;
// Get the direction that the hand is facing in the world
var direction = Vec3.multiplyQbyV(pose.orientation, [0, 1, 0]);
pickRayTypeHandler(start, direction);
if (_this.currentPick) {
_this.eventHandler(_this.currentPick, _this.intersection);
}
}
// The following two functions are a modified version of what's found in scripts/system/libraries/controllers.js
// Utility function for the ControllerWorldLocation offset
function getGrabPointSphereOffset(handController) {
// These values must match what's in scripts/system/libraries/controllers.js
// x = upward, y = forward, z = lateral
var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 };
var offset = GRAB_POINT_SPHERE_OFFSET;
if (handController === Controller.Standard.LeftHand) {
offset = {
x: -GRAB_POINT_SPHERE_OFFSET.x,
y: GRAB_POINT_SPHERE_OFFSET.y,
z: GRAB_POINT_SPHERE_OFFSET.z
};
}
return Vec3.multiply(MyAvatar.sensorToWorldScale, offset);
}
// controllerWorldLocation is where the controller would be, in-world, with an added offset
function getControllerWorldLocation(handController, doOffset) {
var orientation;
var position;
var valid = false;
if (handController >= 0) {
var pose = Controller.getPoseValue(handController);
valid = pose.valid;
var controllerJointIndex;
if (pose.valid) {
if (handController === Controller.Standard.RightHand) {
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND");
} else {
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
}
orientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(controllerJointIndex));
position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(controllerJointIndex)));
// add to the real position so the grab-point is out in front of the hand, a bit
if (doOffset) {
var offset = getGrabPointSphereOffset(handController);
position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, offset));
}
} else if (!HMD.isHandControllerAvailable()) {
// NOTE: keep _this offset in sync with scripts/system/controllers/handControllerPointer.js:493
var VERTICAL_HEAD_LASER_OFFSET = 0.1 * MyAvatar.sensorToWorldScale;
position = Vec3.sum(Camera.position, Vec3.multiplyQbyV(Camera.orientation, { x: 0, y: VERTICAL_HEAD_LASER_OFFSET, z: 0 }));
orientation = Quat.multiply(Camera.orientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }));
valid = true;
}
}
return {
position: position,
translation: position,
orientation: orientation,
rotation: orientation,
valid: valid
};
}
// Handle if the uuid picked on is new or different
function handleUUID(uuid){
if (!_this.lastPick && !_this.currentPick) {
_this.currentPick = uuid;
_this.lastPick = uuid;
} else {
_this.lastPick = _this.currentPick;
_this.currentPick = uuid;
}
}
function pickRayTypeHandler(pickRay){
// Handle if pickray is system generated or user generated
if (arguments.length === 2) {
pickRay = { origin: arguments[0], direction: arguments[1] };
}
// Each different ray pick type needs a different findRayIntersection function
switch (_this.rayType) {
case 'avatar':
var avatarIntersection = AvatarList.findRayIntersection(pickRay, [], [MyAvatar.sessionUUID], false);
_this.intersection = avatarIntersection;
handleUUID(avatarIntersection.avatarID);
break;
case 'local':
var overlayIntersection = Overlays.findRayIntersection(pickRay, [], []);
_this.intersection = overlayIntersection;
handleUUID(overlayIntersection.overlayID);
break;
case 'entity':
var entityIntersection = Entities.findRayIntersection(pickRay, [], []);
_this.intersection = entityIntersection;
handleUUID(entityIntersection.avatarID);
break;
default:
console.log("ray type not handled");
}
}
// Handle the interaction when in desktop and a mouse is pressed
function mousePressHandler(event) {
if (HMD.active || !event.isLeftButton) {
return;
}
var pickRay = Camera.computePickRay(event.x, event.y);
pickRayTypeHandler(pickRay);
if (_this.currentPick) {
_this.eventHandler(_this.currentPick, _this.intersection);
}
}
// Function to call when double press is singled
function doublePressHandler(event) {
mousePressHandler(event);
}
// #endregion
// *************************************
// END UTILITY
// *************************************
// *************************************
// START API
// *************************************
// #region API
// After setup is given, this gets the Controller ready to be enabled
function create(){
_this.mapping = Controller.newMapping(_this.mappingName);
_this.mapping.from(Controller.Standard.LTClick).to(function (value) {
if (value === 0) {
return;
}
getUUIDFromLaser(Controller.Standard.LeftHand);
});
_this.mapping.from(Controller.Standard.RTClick).to(function (value) {
if (value === 0) {
return;
}
getUUIDFromLaser(Controller.Standard.RightHand);
});
return _this;
}
// Set type of raypick for what kind of uuids to return
function setType(type){
_this.rayType = type;
return _this;
}
// Set if double presses should register as well
function setShouldDoublePress(shouldDoublePress){
_this.shouldDoublePress = shouldDoublePress;
return _this;
}
// Set the mapping name for the controller
function setMapName(name) {
_this.mappingName = name;
return _this;
}
// Enables mouse press and trigger events
function enable(){
if (!_this.controllEnabled) {
Controller.mousePressEvent.connect(mousePressHandler);
if (_this.shouldDoublePress) {
Controller.mouseDoublePressEvent.connect(doublePressHandler);
}
Controller.enableMapping(_this.mappingName);
_this.controllEnabled = true;
return _this;
}
return -1;
}
// Disable the controller and mouse press
function disable(){
if (_this.controllEnabled) {
Controller.mousePressEvent.disconnect(mousePressHandler);
if (_this.shouldDoublePress){
Controller.mouseDoublePressEvent.disconnect(doublePressHandler);
}
Controller.disableMapping(_this.mappingName);
_this.controllEnabled = false;
return _this;
}
return -1;
}
// Synonym for disable
function destroy(){
_this.disable();
}
// Register the function to be called on a click
function registerEventHandler(fn){
_this.eventHandler = fn;
return _this;
}
// #endregion
// *************************************
// END API
// *************************************
PickRayController.prototype = {
create: create,
setType: setType,
setShouldDoublePress: setShouldDoublePress,
setMapName: setMapName,
enable: enable,
disable: disable,
destroy: destroy,
registerEventHandler: registerEventHandler
};
module.exports = PickRayController;

View file

@ -0,0 +1,224 @@
//
// Simplified Nametag
// textHelper.js
// Created by Milad Nazeri on 2019-03-08
// Copyright 2019 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
//
// Module to help calculate text size
//
// *************************************
// START MAPS
// *************************************
// #region MAPS
var charMap = {
a: 0.05,
b: 0.051,
c: 0.05,
d: 0.051,
e: 0.05,
f: 0.035,
g: 0.051,
h: 0.051,
i: 0.025,
j: 0.025,
k: 0.05,
l: 0.025,
m: 0.0775,
n: 0.051,
o: 0.051,
p: 0.051,
q: 0.051,
r: 0.035,
s: 0.05,
t: 0.035,
u: 0.051,
v: 0.05,
w: 0.07,
x: 0.05,
y: 0.05,
z: 0.05,
A: 0.06,
B: 0.06,
C: 0.06,
D: 0.06,
E: 0.05,
F: 0.05,
G: 0.06,
H: 0.0625,
I: 0.0275,
J: 0.05,
K: 0.06,
L: 0.05,
M: 0.075,
N: 0.0625,
O: 0.0625,
P: 0.06,
Q: 0.0625,
R: 0.06,
S: 0.06,
T: 0.06,
U: 0.06,
V: 0.06,
W: 0.075,
X: 0.06,
Y: 0.06,
Z: 0.06
};
var symbolMap = {
"!": 0.025,
"@": 0.08,
"#": 0.07,
"$": 0.058,
"%": 0.07,
"^": 0.04,
"&": 0.06,
"*": 0.04,
"(": 0.04,
")": 0.04,
"_": 0.041,
"{": 0.034,
"}": 0.034,
"/": 0.04,
"|": 0.02,
"<": 0.049,
">": 0.049,
"[": 0.0300,
"]": 0.0300,
".": 0.0260,
",": 0.0260,
"?": 0.048,
"~": 0.0610,
"`": 0.0310,
"+": 0.0510,
"=": 0.0510
};
// #endregion
// *************************************
// END MAPS
// *************************************
var _this = null;
function TextHelper(){
_this = this;
this.text = "";
this.textArray = "";
this.lineHeight = 0;
this.totalTextLength = 0;
this.scaler = 1.0;
}
// *************************************
// START UTILITY
// *************************************
// #region UTILITY
// Split the string into a text array to be operated on
function createTextArray(){
_this.textArray = _this.text.split("");
}
// Account for the text length
function adjustForScale(defaultTextLength){
_this.totalTextLength = defaultTextLength * _this.scaler;
}
// #endregion
// *************************************
// END UTILITY
// *************************************
// #endregion
// *************************************
// END name
// *************************************
// *************************************
// START API
// *************************************
// #region API
// Set the text that needs to be calculated on
function setText(newText){
_this.text = newText;
createTextArray();
return _this;
}
// Set the line height which helps calculate the font size
var DEFAULT_LINE_HEIGHT = 0.1;
function setLineHeight(newLineHeight){
_this.lineHeight = newLineHeight;
_this.scaler = _this.lineHeight / DEFAULT_LINE_HEIGHT;
return _this;
}
// Calculate the sign dimensions
var DEFAULT_CHAR_SIZE = 0.025;
var NUMBER = 0.05;
var DIGIT_REGEX = /\d/g;
var WHITE_SPACE_REGEX = /[ ]/g;
var SPACE = 0.018;
function getTotalTextLength(){
// Map the string array to it's sizes
var lengthArray = _this.textArray.map(function(letter){
if (charMap[letter]){
return charMap[letter];
} else if (letter.match(DIGIT_REGEX)){
return NUMBER;
} else if (symbolMap[letter]) {
return symbolMap[letter];
} else if (letter.match(WHITE_SPACE_REGEX)) {
return SPACE;
} else {
return DEFAULT_CHAR_SIZE;
}
});
// add up all the values in the array
var defaultTextLength = lengthArray.reduce(function(prev, curr){
return prev + curr;
}, 0);
adjustForScale(defaultTextLength);
return _this.totalTextLength;
}
// #endregion
// *************************************
// END API
// *************************************
TextHelper.prototype = {
setText: setText,
setLineHeight: setLineHeight,
getTotalTextLength: getTotalTextLength
};
module.exports = TextHelper;
// var text = new TextHelper();
// text.setText("lowbar");
// text.setLineHeight("0.1");
// text.getTotalTextLength();

View file

@ -0,0 +1,96 @@
//
// Simplified Nametag
// Created by Milad Nazeri on 2019-02-16
// Copyright 2019 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
//
// Click on someone to get a nametag for them
//
var PickRayController = Script.require('./resources/modules/pickRayController.js?' + Date.now());
var NameTagListManager = Script.require('./resources/modules/nameTagListManager.js?' + Date.now());
var pickRayController = new PickRayController();
var nameTagListManager = new NameTagListManager();
// Handles avatar being solo'd
pickRayController
.registerEventHandler(selectAvatar)
.setType("avatar")
.setMapName("hifi_simplifiedNametag")
.setShouldDoublePress(true)
.create()
.enable();
function selectAvatar(uuid, intersection) {
nameTagListManager.handleSelect(uuid, intersection);
}
// Handles reset of list if you change domains
function onDomainChange() {
nameTagListManager.reset();
}
// Handles removing an avatar from the list if they leave the domain
function onAvatarRemoved(uuid) {
nameTagListManager.maybeRemove(uuid);
}
// Automatically add an avatar if they come into the domain. Mainly used for alwaysOn mode.
function onAvatarAdded(uuid) {
nameTagListManager.maybeAdd(uuid);
}
// Called on init
var avatarNametagMode;
function create() {
nameTagListManager.create();
handleAvatarNametagMode(Settings.getValue("simplifiedNametag/avatarNametagMode", "on"));
Window.domainChanged.connect(onDomainChange);
AvatarManager.avatarRemovedEvent.connect(onAvatarRemoved);
AvatarManager.avatarAddedEvent.connect(onAvatarAdded);
}
// Called when the script is closing
function destroy() {
nameTagListManager.destroy();
pickRayController.destroy();
Window.domainChanged.disconnect(onDomainChange);
AvatarManager.avatarRemovedEvent.disconnect(onAvatarRemoved);
AvatarManager.avatarAddedEvent.disconnect(onAvatarAdded);
}
// chose which mode you want the nametags in. On, off, or alwaysOn.
function handleAvatarNametagMode(newAvatarNameTagMode) {
avatarNametagMode = newAvatarNameTagMode;
nameTagListManager.handleAvatarNametagMode(avatarNametagMode);
Settings.setValue("simplifiedNametag/avatarNametagMode", avatarNametagMode);
}
// *************************************
// START api
// *************************************
// #region api
module.exports = {
create: create,
destroy: destroy,
handleAvatarNametagMode: handleAvatarNametagMode
};
// #endregion
// *************************************
// END api
// *************************************

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="353" height="95" fill="none" version="1.1" viewBox="0 0 353 95" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<rect width="353" height="95" rx="10" fill="#fff"/>
<g transform="translate(-64)" clip-path="url(#clip0)">
<path d="m115.08 28.505-2.544 2.5441c-3.354-3.7005-8.095-6.129-13.53-6.129-5.2039 0-9.8295 2.3128-13.183 5.7821l-2.5441-2.5441c3.9318-4.1631 9.5982-6.8229 15.727-6.8229 6.36 0 12.142 2.7754 16.074 7.1698zm-8.442 8.4432 2.545-2.5441c-2.429-2.891-6.129-4.7412-10.177-4.7412-3.9318 0-7.401 1.7346-9.8295 4.3943l2.5441 2.5441c1.8503-1.9659 4.3944-3.3536 7.2854-3.3536 3.1218 0 5.8978 1.3877 7.6318 3.7005zm-3.291 5.3342v-0.5659c0-2.2639-1.915-4.075-4.1677-4.075-2.253 0-4.168 1.8111-4.168 4.075v7.8103zm-7.5234 16.37-3.3507 2.9444c1.5344 1.8207 3.9944 2.985 6.7063 2.985 4.6188 0 8.4488-3.3958 8.4488-7.584v-5.886c0-1.132 0.788-2.0375 1.915-2.0375 1.014 0 2.027 0.7923 2.027 1.9243v5.9992c0 5.7729-4.505 10.527-10.363 11.432v3.8486h4.956c1.127 0 2.028 0.9055 2.028 2.0375 0 1.1319-0.901 2.0374-2.028 2.0374h-13.855c-1.1265 0-2.0276-0.9055-2.0276-2.0374 0-1.132 0.9011-2.0375 2.0276-2.0375h4.8439v-3.8486c-3.0801-0.4072-5.8103-1.9866-7.687-4.1899l-3.3526 2.9461c-0.7885 0.6792-2.0277 0.566-2.7036-0.2264l-0.2253-0.2264c-0.6759-0.7923-0.5632-2.0374 0.2253-2.7166l28.613-25.355c0.789-0.6791 2.028-0.5659 2.704 0.2264l0.225 0.2264c0.676 0.9055 0.563 2.0375-0.113 2.8298l-11.49 10.097v4.1636c0 2.2638-1.915 4.0749-4.1677 4.0749-1.4027 0-2.6074-0.6368-3.3557-1.6271zm-6.8954-9.577c1.0138 0 1.8023 0.9056 1.8023 2.0375v2.2639l-3.9427 3.509v-5.9993c0.1127-1.0187 1.0139-1.9243 2.1404-1.8111z" clip-rule="evenodd" fill="#b20012" fill-rule="evenodd"/>
</g>
<path d="m68.308 77h9.6v-43.2l17.36 43.2h3.44l17.04-43.2v43.2h10.64v-57.2h-14.08l-14.72 39.04-15.04-39.04h-14.24zm93.172 0.8c15.12 0 23.2-8.24 23.2-23.2v-34.8h-10.72v33.92c0 11.84-4.16 15.52-12.4 15.52-8.48 0-11.84-4.32-11.84-14.8v-34.64h-10.72v35.44c0 14.72 8.16 22.56 22.48 22.56zm47.253-0.8h10.72v-48.8h15.92v-8.4h-42.56v8.4h15.92zm35.2 0h37.12v-8.4h-26.48v-16.72h20.16v-8h-20.16v-15.68h25.12v-8.4h-35.76zm57.359-8.4v-40.4h6.16c13.44 0 19.04 7.44 19.04 19.76v0.64c0 12.64-6.16 20-18.96 20zm-10.64 8.4h17.2c20.08 0 29.76-11.84 29.76-28.48v-0.72c0-16.64-9.68-28-29.6-28h-17.36z" fill="#b20012"/>
<defs>
<clipPath id="clip0">
<rect transform="translate(70.598 21.335)" width="55.508" height="55.508" fill="#fff"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,4 @@
<svg width="553" height="553" viewBox="0 0 553 553" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="276.5" cy="276.5" r="276.5" fill="white"/>
<path d="M292.112 239.537V388.865C292.957 396.799 291.001 404.778 286.583 411.421C282.164 418.064 275.561 422.953 267.916 425.243C263.762 426.761 259.413 427.679 255 427.971C249.454 428.205 243.94 427.025 238.976 424.544C234.011 422.062 229.759 418.359 226.619 413.784C220.979 408.145 212.61 399.415 201.695 387.592C190.779 375.77 182.593 367.039 176.953 361.219C175.486 359.629 173.471 358.654 171.313 358.491H155.304C148.119 358.643 140.935 358.156 133.836 357.035C125.817 355.846 118.507 351.771 113.279 345.577C107.506 339.98 104.172 332.334 104 324.296V220.621C103.989 215.99 105.012 211.415 106.995 207.23C108.978 203.045 111.87 199.355 115.462 196.43C122.88 189.865 132.48 186.298 142.387 186.426H170.949L172.223 185.153C173.774 184.335 175.237 183.36 176.589 182.243C188.778 169.875 205.515 152.414 226.255 129.678C231.222 123.895 237.881 119.818 245.292 118.023C252.702 116.228 260.489 116.806 267.553 119.674C275.013 121.964 281.472 126.722 285.869 133.168C290.266 139.613 292.34 147.362 291.749 155.142L292.112 239.537ZM240.809 169.875L196.783 218.257C195.369 219.895 193.644 221.237 191.708 222.205C189.772 223.173 187.663 223.747 185.504 223.895H142.751V319.203H186.777C189.465 319.135 192.074 320.113 194.054 321.931C199.375 328.283 205.022 334.354 210.973 340.12C218.614 347.759 224.254 353.761 228.074 358.309L240.809 371.223C243.866 375.22 247.21 378.989 250.815 382.499L252.27 383.955V158.598L240.809 169.875ZM452.572 218.257C451.101 216.263 449.182 214.643 446.97 213.527C444.758 212.411 442.315 211.829 439.837 211.829C437.36 211.829 434.916 212.411 432.704 213.527C430.492 214.643 428.574 216.263 427.102 218.257L345.963 299.196C344 300.708 342.41 302.65 341.317 304.874C340.223 307.097 339.654 309.541 339.654 312.019C339.654 314.496 340.223 316.941 341.317 319.164C342.41 321.387 344 323.33 345.963 324.842C347.454 326.836 349.389 328.456 351.616 329.572C353.842 330.687 356.298 331.268 358.789 331.268C361.279 331.268 363.735 330.687 365.962 329.572C368.188 328.456 370.124 326.836 371.615 324.842L452.572 243.903C454.567 242.412 456.187 240.477 457.303 238.251C458.419 236.025 459 233.569 459 231.08C459 228.59 458.419 226.134 457.303 223.908C456.187 221.682 454.567 219.747 452.572 218.257V218.257ZM452.572 324.842C454.567 323.351 456.187 321.416 457.303 319.19C458.419 316.964 459 314.509 459 312.019C459 309.529 458.419 307.073 457.303 304.847C456.187 302.621 454.567 300.686 452.572 299.196L371.615 218.257C370.124 216.262 368.188 214.642 365.962 213.526C363.735 212.411 361.279 211.83 358.789 211.83C356.298 211.83 353.842 212.411 351.616 213.526C349.389 214.642 347.454 216.262 345.963 218.257C344 219.769 342.41 221.711 341.317 223.934C340.223 226.157 339.654 228.602 339.654 231.08C339.654 233.557 340.223 236.002 341.317 238.225C342.41 240.448 344 242.39 345.963 243.903L427.102 324.842C428.574 326.835 430.492 328.455 432.704 329.571C434.916 330.688 437.36 331.269 439.837 331.269C442.315 331.269 444.758 330.688 446.97 329.571C449.182 328.455 451.101 326.835 452.572 324.842V324.842Z" fill="#B20012"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,515 @@
"use strict";
/* jslint vars: true, plusplus: true */
//
// simplifiedUI.js
//
// Authors: Wayne Chen & Zach Fox
// Created on: 5/1/2019
// Copyright 2019 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
//
// START CONFIG OPTIONS
var DOCKED_QML_SUPPORTED = true;
var REMOVE_EXISTING_UI = true;
var TOOLBAR_NAME = "com.highfidelity.interface.toolbar.system";
var DEFAULT_SCRIPTS_PATH_PREFIX = ScriptDiscoveryService.defaultScriptsPath + "/";
// END CONFIG OPTIONS
var DEFAULT_SCRIPTS_SEPARATE = [
DEFAULT_SCRIPTS_PATH_PREFIX + "system/controllers/controllerScripts.js"
];
function loadSeparateDefaults() {
for (var i in DEFAULT_SCRIPTS_SEPARATE) {
Script.load(DEFAULT_SCRIPTS_SEPARATE[i]);
}
}
var DEFAULT_SCRIPTS_COMBINED = [
DEFAULT_SCRIPTS_PATH_PREFIX + "system/request-service.js",
DEFAULT_SCRIPTS_PATH_PREFIX + "system/progress.js",
DEFAULT_SCRIPTS_PATH_PREFIX + "system/away.js"
];
function runDefaultsTogether() {
for (var i in DEFAULT_SCRIPTS_COMBINED) {
Script.include(DEFAULT_SCRIPTS_COMBINED[i]);
}
}
// Uncomment this out once the work is actually complete.
// Until then, users are required to access some functionality from the top menu bar.
//var MENU_NAMES = ["File", "Edit", "Display", "View", "Navigate", "Settings", "Developer", "Help"];
var MENU_NAMES = ["File", "Edit", "View", "Navigate", "Help"];
function removeDesktopMenu() {
MENU_NAMES.forEach(function(menu) {
Menu.removeMenu(menu);
});
}
function handleUpdateAvatarThumbnailURL(avatarThumbnailURL) {
if (topBarWindow) {
topBarWindow.sendToQml({
"source": "simplifiedUI.js",
"method": "updateAvatarThumbnailURL",
"data": {
"avatarThumbnailURL": avatarThumbnailURL
}
});
}
}
var AVATAR_APP_MESSAGE_SOURCE = "AvatarApp.qml";
function onMessageFromAvatarApp(message) {
if (message.source !== AVATAR_APP_MESSAGE_SOURCE) {
return;
}
switch (message.method) {
case "updateAvatarThumbnailURL":
handleUpdateAvatarThumbnailURL(message.data.avatarThumbnailURL);
break;
default:
console.log("Unrecognized message from " + AVATAR_APP_MESSAGE_SOURCE + ": " + JSON.stringify(message));
break;
}
}
function onAvatarAppClosed() {
if (avatarAppWindow) {
avatarAppWindow.fromQml.disconnect(onMessageFromAvatarApp);
avatarAppWindow.closed.disconnect(onAvatarAppClosed);
}
avatarAppWindow = false;
}
var AVATAR_APP_QML_PATH = Script.resourcesPath() + "qml/hifi/simplifiedUI/avatarApp/AvatarApp.qml";
var AVATAR_APP_WINDOW_TITLE = "Your Avatars";
var AVATAR_APP_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE;
var AVATAR_APP_WIDTH_PX = 480;
var AVATAR_APP_HEIGHT_PX = 615;
var avatarAppWindow = false;
function toggleAvatarApp() {
if (avatarAppWindow) {
avatarAppWindow.close();
// This really shouldn't be necessary.
// This signal really should automatically be called by the signal handler set up below.
// But fixing that requires an engine change, so this workaround will do.
onAvatarAppClosed();
return;
}
avatarAppWindow = Desktop.createWindow(AVATAR_APP_QML_PATH, {
title: AVATAR_APP_WINDOW_TITLE,
presentationMode: AVATAR_APP_PRESENTATION_MODE,
size: {
x: AVATAR_APP_WIDTH_PX,
y: AVATAR_APP_HEIGHT_PX
}
});
avatarAppWindow.fromQml.connect(onMessageFromAvatarApp);
avatarAppWindow.closed.connect(onAvatarAppClosed);
}
function handleAvatarNametagMode(newAvatarNametagMode) {
simplifiedNametag.handleAvatarNametagMode(newAvatarNametagMode);
}
var SETTINGS_APP_MESSAGE_SOURCE = "SettingsApp.qml";
function onMessageFromSettingsApp(message) {
if (message.source !== SETTINGS_APP_MESSAGE_SOURCE) {
return;
}
switch (message.method) {
case "handleAvatarNametagMode":
handleAvatarNametagMode(message.avatarNametagMode);
break;
default:
console.log("Unrecognized message from " + SETTINGS_APP_MESSAGE_SOURCE + ": " + JSON.stringify(message));
break;
}
}
function onSettingsAppClosed() {
if (settingsAppWindow) {
settingsAppWindow.fromQml.disconnect(onMessageFromSettingsApp);
settingsAppWindow.closed.disconnect(onSettingsAppClosed);
}
settingsAppWindow = false;
}
var SETTINGS_APP_QML_PATH = Script.resourcesPath() + "qml/hifi/simplifiedUI/settingsApp/SettingsApp.qml";
var SETTINGS_APP_WINDOW_TITLE = "Settings";
var SETTINGS_APP_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE;
var SETTINGS_APP_WIDTH_PX = 480;
var SETTINGS_APP_HEIGHT_PX = 615;
var settingsAppWindow = false;
function toggleSettingsApp() {
if (settingsAppWindow) {
settingsAppWindow.close();
// This really shouldn't be necessary.
// This signal really should automatically be called by the signal handler set up below.
// But fixing that requires an engine change, so this workaround will do.
onSettingsAppClosed();
return;
}
settingsAppWindow = Desktop.createWindow(SETTINGS_APP_QML_PATH, {
title: SETTINGS_APP_WINDOW_TITLE,
presentationMode: SETTINGS_APP_PRESENTATION_MODE,
size: {
x: SETTINGS_APP_WIDTH_PX,
y: SETTINGS_APP_HEIGHT_PX
}
});
settingsAppWindow.fromQml.connect(onMessageFromSettingsApp);
settingsAppWindow.closed.connect(onSettingsAppClosed);
}
function maybeDeleteOutputDeviceMutedOverlay() {
if (outputDeviceMutedOverlay) {
Overlays.deleteOverlay(outputDeviceMutedOverlay);
outputDeviceMutedOverlay = false;
}
}
var outputDeviceMutedOverlay = false;
var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX = 300;
var OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX = 20;
var OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX = 20;
function updateOutputDeviceMutedOverlay(isMuted) {
if (isMuted) {
var props = {
imageURL: Script.resolvePath("images/outputDeviceMuted.svg"),
alpha: 0.5
};
var overlayDims = OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX;
props.x = Window.innerWidth / 2 - overlayDims / 2;
props.y = Window.innerHeight / 2 - overlayDims / 2;
var outputDeviceMutedOverlayBottomY = props.y + overlayDims;
var inputDeviceMutedOverlayTopY = getInputDeviceMutedOverlayTopY();
if (outputDeviceMutedOverlayBottomY + OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX > inputDeviceMutedOverlayTopY) {
overlayDims = 2 * (inputDeviceMutedOverlayTopY - Window.innerHeight / 2 - OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX);
}
if (overlayDims + OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX > Window.innerWidth) {
overlayDims = Math.min(Window.innerWidth - OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX, overlayDims);
} else {
overlayDims = Math.min(OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX, overlayDims);
}
props.width = overlayDims;
props.height = overlayDims;
props.x = Window.innerWidth / 2 - overlayDims / 2;
props.y = Window.innerHeight / 2 - overlayDims / 2;
if (outputDeviceMutedOverlay) {
Overlays.editOverlay(outputDeviceMutedOverlay, props);
} else {
outputDeviceMutedOverlay = Overlays.addOverlay("image", props);
}
} else {
maybeDeleteOutputDeviceMutedOverlay();
}
}
var savedAvatarGain = Audio.getAvatarGain();
var savedInjectorGain = Audio.getInjectorGain();
var savedLocalInjectorGain = Audio.getLocalInjectorGain();
var savedSystemInjectorGain = Audio.getSystemInjectorGain();
function setOutputMuted(outputMuted) {
updateOutputDeviceMutedOverlay(outputMuted);
if (outputMuted) {
savedAvatarGain = Audio.getAvatarGain();
savedInjectorGain = Audio.getInjectorGain();
savedLocalInjectorGain = Audio.getLocalInjectorGain();
savedSystemInjectorGain = Audio.getSystemInjectorGain();
Audio.setAvatarGain(-60);
Audio.setInjectorGain(-60);
Audio.setLocalInjectorGain(-60);
Audio.setSystemInjectorGain(-60);
} else {
if (savedAvatarGain === -60) {
savedAvatarGain = 0;
}
Audio.setAvatarGain(savedAvatarGain);
if (savedInjectorGain === -60) {
savedInjectorGain = 0;
}
Audio.setInjectorGain(savedInjectorGain);
if (savedLocalInjectorGain === -60) {
savedLocalInjectorGain = 0;
}
Audio.setLocalInjectorGain(savedLocalInjectorGain);
if (savedSystemInjectorGain === -60) {
savedSystemInjectorGain = 0;
}
Audio.setSystemInjectorGain(savedSystemInjectorGain);
}
}
var TOP_BAR_MESSAGE_SOURCE = "SimplifiedTopBar.qml";
function onMessageFromTopBar(message) {
if (message.source !== TOP_BAR_MESSAGE_SOURCE) {
return;
}
switch (message.method) {
case "toggleAvatarApp":
toggleAvatarApp();
break;
case "toggleSettingsApp":
toggleSettingsApp();
break;
case "setOutputMuted":
setOutputMuted(message.data.outputMuted);
break;
default:
console.log("Unrecognized message from " + TOP_BAR_MESSAGE_SOURCE + ": " + JSON.stringify(message));
break;
}
}
function onTopBarClosed() {
if (topBarWindow) {
topBarWindow.fromQml.disconnect(onMessageFromTopBar);
topBarWindow.closed.disconnect(onTopBarClosed);
}
topBarWindow = false;
}
function isOutputMuted() {
return Audio.getAvatarGain() === -60 && Audio.getInjectorGain() === -60 && Audio.getLocalInjectorGain() === -60 && Audio.getSystemInjectorGain() === -60;
}
var TOP_BAR_QML_PATH = Script.resourcesPath() + "qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml";
var TOP_BAR_WINDOW_TITLE = "Simplified Top Bar";
var TOP_BAR_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE;
var TOP_BAR_WIDTH_PX = Window.innerWidth;
var TOP_BAR_HEIGHT_PX = 48;
var topBarWindow = false;
function loadSimplifiedTopBar() {
var windowProps = {
title: TOP_BAR_WINDOW_TITLE,
presentationMode: TOP_BAR_PRESENTATION_MODE,
size: {
x: TOP_BAR_WIDTH_PX,
y: TOP_BAR_HEIGHT_PX
}
};
if (DOCKED_QML_SUPPORTED) {
windowProps.presentationWindowInfo = {
dockArea: Desktop.DockArea.TOP
};
} else {
windowProps.position = {
x: Window.x,
y: Window.y
};
}
topBarWindow = Desktop.createWindow(TOP_BAR_QML_PATH, windowProps);
topBarWindow.fromQml.connect(onMessageFromTopBar);
topBarWindow.closed.connect(onTopBarClosed);
topBarWindow.sendToQml({
"source": "simplifiedUI.js",
"method": "updateOutputMuted",
"data": {
"outputMuted": isOutputMuted()
}
})
}
var pausedScriptList = [];
var SCRIPT_NAME_WHITELIST = ["simplifiedUI.js", "statusIndicator.js"];
function pauseCurrentScripts() {
var currentlyRunningScripts = ScriptDiscoveryService.getRunning();
for (var i = 0; i < currentlyRunningScripts.length; i++) {
var currentScriptObject = currentlyRunningScripts[i];
if (SCRIPT_NAME_WHITELIST.indexOf(currentScriptObject.name) === -1) {
ScriptDiscoveryService.stopScript(currentScriptObject.url);
pausedScriptList.push(currentScriptObject.url);
}
}
}
function maybeDeleteInputDeviceMutedOverlay() {
if (inputDeviceMutedOverlay) {
Overlays.deleteOverlay(inputDeviceMutedOverlay);
inputDeviceMutedOverlay = false;
}
}
function getInputDeviceMutedOverlayTopY() {
return (Window.innerHeight - INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_Y_PX - INPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX);
}
var inputDeviceMutedOverlay = false;
var INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_X_PX = 353;
var INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_Y_PX = 95;
var INPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX = 20;
function updateInputDeviceMutedOverlay(isMuted) {
if (isMuted) {
var props = {
imageURL: Script.resolvePath("images/inputDeviceMuted.svg"),
alpha: 0.5
};
props.width = INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_X_PX;
props.height = INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_Y_PX;
props.x = Window.innerWidth / 2 - INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_X_PX / 2;
props.y = getInputDeviceMutedOverlayTopY();
if (inputDeviceMutedOverlay) {
Overlays.editOverlay(inputDeviceMutedOverlay, props);
} else {
inputDeviceMutedOverlay = Overlays.addOverlay("image", props);
}
} else {
maybeDeleteInputDeviceMutedOverlay();
}
}
function onDesktopInputDeviceMutedChanged(isMuted) {
updateInputDeviceMutedOverlay(isMuted);
}
function onGeometryChanged(rect) {
updateInputDeviceMutedOverlay(Audio.muted);
updateOutputDeviceMutedOverlay(isOutputMuted());
if (topBarWindow && !DOCKED_QML_SUPPORTED) {
topBarWindow.size = {
"x": rect.width,
"y": TOP_BAR_HEIGHT_PX
};
topBarWindow.position = {
"x": rect.x,
"y": rect.y
};
}
}
function ensureFirstPersonCameraInHMD(isHMDMode) {
if (isHMDMode) {
Camera.setModeString("first person");
}
}
var simplifiedNametag = Script.require("../simplifiedNametag/simplifiedNametag.js");
var oldShowAudioTools;
var oldShowBubbleTools;
function startup() {
if (REMOVE_EXISTING_UI) {
pauseCurrentScripts();
removeDesktopMenu();
runDefaultsTogether();
loadSeparateDefaults();
if (!HMD.active) {
var toolbar = Toolbars.getToolbar(TOOLBAR_NAME);
toolbar.writeProperty("visible", false);
}
}
loadSimplifiedTopBar();
simplifiedNametag.create();
updateInputDeviceMutedOverlay(Audio.muted);
updateOutputDeviceMutedOverlay(isOutputMuted());
Audio.mutedDesktopChanged.connect(onDesktopInputDeviceMutedChanged);
Window.geometryChanged.connect(onGeometryChanged);
HMD.displayModeChanged.connect(ensureFirstPersonCameraInHMD);
oldShowAudioTools = AvatarInputs.showAudioTools;
AvatarInputs.showAudioTools = false;
oldShowBubbleTools = AvatarInputs.showBubbleTools;
AvatarInputs.showBubbleTools = false;
}
function restoreScripts() {
pausedScriptList.forEach(function(url) {
ScriptDiscoveryService.loadScript(url);
});
pausedScriptList = [];
}
function shutdown() {
restoreScripts();
if (REMOVE_EXISTING_UI) {
Window.confirm("You'll have to restart Interface to get full functionality back. Clicking yes or no will dismiss this dialog.");
if (!HMD.active) {
var toolbar = Toolbars.getToolbar(TOOLBAR_NAME);
toolbar.writeProperty("visible", true);
}
}
if (topBarWindow) {
topBarWindow.close();
}
if (avatarAppWindow) {
avatarAppWindow.close();
}
if (settingsAppWindow) {
settingsAppWindow.close();
}
maybeDeleteInputDeviceMutedOverlay();
maybeDeleteOutputDeviceMutedOverlay();
simplifiedNametag.destroy();
Audio.mutedDesktopChanged.disconnect(onDesktopInputDeviceMutedChanged);
Window.geometryChanged.disconnect(onGeometryChanged);
HMD.displayModeChanged.disconnect(ensureFirstPersonCameraInHMD);
AvatarInputs.showAudioTools = oldShowAudioTools;
AvatarInputs.showBubbleTools = oldShowBubbleTools;
}
Script.scriptEnding.connect(shutdown);
startup();