diff --git a/.eslintrc.js b/.eslintrc.js
index 54ff0a1268..5667a04984 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -68,7 +68,7 @@ module.exports = {
"eqeqeq": ["error", "always"],
"indent": ["error", 4, { "SwitchCase": 1 }],
"keyword-spacing": ["error", { "before": true, "after": true }],
- "max-len": ["error", 192, 4],
+ "max-len": ["error", 128, 4],
"new-cap": ["error"],
"no-floating-decimal": ["error"],
//"no-magic-numbers": ["error", { "ignore": [0, 1], "ignoreArrayIndexes": true }],
diff --git a/.gitignore b/.gitignore
index 072e6001da..c1eef3817f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,6 +64,10 @@ gvr-interface/libs/*
# ignore files for various dev environments
TAGS
*.sw[po]
+*.qmlc
+
+# ignore QML compilation output
+*.qmlc
# ignore node files for the console
node_modules
diff --git a/README.md b/README.md
index 6294981e9a..e0bbed3105 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,10 @@ Documentation is available at [docs.highfidelity.com](https://docs.highfidelity.
There is also detailed [documentation on our coding standards](https://wiki.highfidelity.com/wiki/Coding_Standards).
+Contributor License Agreement (CLA)
+=========
+Technology companies frequently receive and use code from contributors outside the company's development team. Outside code can be a tremendous resource, but it also carries responsibility. Best practice for accepting outside contributions consists of an Apache-type Contributor License Agreement (CLA). We have modeled the High Fidelity CLA after the CLA that Google presents to developers for contributions to their projects. This CLA does not transfer ownership of code, instead simply granting a non-exclusive right for High Fidelity to use the code you’ve contributed. In that regard, you should be sure you have permission if the work relates to or uses the resources of a company that you work for. You will be asked to sign our CLA when you create your first PR or when the CLA is updated. You can also [review it here](https://gist.githubusercontent.com/hifi-gustavo/fef8f06a8233d42a0040d45c3efb97a9/raw/9981827eb94f0b18666083670b6f6a02929fb402/High%2520Fidelity%2520CLA). We sincerely appreciate your contribution and efforts toward the success of the platform.
+
Build Instructions
=========
All information required to build is found in the [build guide](BUILD.md).
diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp
index 5539d6a0bb..1868ccfafe 100644
--- a/assignment-client/src/AssignmentClientMonitor.cpp
+++ b/assignment-client/src/AssignmentClientMonitor.cpp
@@ -28,6 +28,10 @@
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
const int WAIT_FOR_CHILD_MSECS = 1000;
+#ifdef Q_OS_WIN
+HANDLE PROCESS_GROUP = createProcessGroup();
+#endif
+
AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks,
const unsigned int minAssignmentClientForks,
const unsigned int maxAssignmentClientForks,
@@ -202,6 +206,10 @@ void AssignmentClientMonitor::spawnChildClient() {
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
+#ifdef Q_OS_WIN
+ addProcessToGroup(PROCESS_GROUP, assignmentClient->processId());
+#endif
+
QString stdoutPath, stderrPath;
if (_wantsChildFileLogging) {
diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp
index e7d86c824e..ca0f222e0c 100644
--- a/assignment-client/src/assets/AssetServer.cpp
+++ b/assignment-client/src/assets/AssetServer.cpp
@@ -50,9 +50,9 @@ static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000;
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
-static const QStringList BAKEABLE_MODEL_EXTENSIONS = {"fbx"};
+static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" };
static QStringList BAKEABLE_TEXTURE_EXTENSIONS;
-static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = {"js"};
+static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = {};
static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx";
static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx";
static const QString BAKED_SCRIPT_SIMPLE_NAME = "asset.js";
diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index 9ed6c7fdbc..7f088d8183 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -29,6 +29,7 @@
#include
#include
+#include "AudioLogging.h"
#include "AudioHelpers.h"
#include "AudioRingBuffer.h"
#include "AudioMixerClientData.h"
@@ -130,7 +131,7 @@ void AudioMixer::queueReplicatedAudioPacket(QSharedPointer mess
PacketType rewrittenType = PacketTypeEnum::getReplicatedPacketMapping().key(message->getType());
if (rewrittenType == PacketType::Unknown) {
- qDebug() << "Cannot unwrap replicated packet type not present in REPLICATED_PACKET_WRAPPING";
+ qCDebug(audio) << "Cannot unwrap replicated packet type not present in REPLICATED_PACKET_WRAPPING";
}
auto replicatedMessage = QSharedPointer::create(audioData, rewrittenType,
@@ -345,7 +346,7 @@ void AudioMixer::sendStatsPacket() {
void AudioMixer::run() {
- qDebug() << "Waiting for connection to domain to request settings from domain-server.";
+ qCDebug(audio) << "Waiting for connection to domain to request settings from domain-server.";
// wait until we have the domain-server settings, otherwise we bail
DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler();
@@ -502,14 +503,14 @@ void AudioMixer::throttle(std::chrono::microseconds duration, int frame) {
int proportionalTerm = 1 + (_trailingMixRatio - TARGET) / 0.1f;
_throttlingRatio += THROTTLE_RATE * proportionalTerm;
_throttlingRatio = std::min(_throttlingRatio, 1.0f);
- qDebug("audio-mixer is struggling (%f mix/sleep) - throttling %f of streams",
- (double)_trailingMixRatio, (double)_throttlingRatio);
+ qCDebug(audio) << "audio-mixer is struggling (" << _trailingMixRatio << "mix/sleep) - throttling"
+ << _throttlingRatio << "of streams";
} else if (_throttlingRatio > 0.0f && _trailingMixRatio <= BACKOFF_TARGET) {
int proportionalTerm = 1 + (TARGET - _trailingMixRatio) / 0.2f;
_throttlingRatio -= BACKOFF_RATE * proportionalTerm;
_throttlingRatio = std::max(_throttlingRatio, 0.0f);
- qDebug("audio-mixer is recovering (%f mix/sleep) - throttling %f of streams",
- (double)_trailingMixRatio, (double)_throttlingRatio);
+ qCDebug(audio) << "audio-mixer is recovering (" << _trailingMixRatio << "mix/sleep) - throttling"
+ << _throttlingRatio << "of streams";
}
}
}
@@ -534,7 +535,7 @@ void AudioMixer::clearDomainSettings() {
}
void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
- qDebug() << "AVX2 Support:" << (cpuSupportsAVX2() ? "enabled" : "disabled");
+ qCDebug(audio) << "AVX2 Support:" << (cpuSupportsAVX2() ? "enabled" : "disabled");
if (settingsObject.contains(AUDIO_THREADING_GROUP_KEY)) {
QJsonObject audioThreadingGroupObject = settingsObject[AUDIO_THREADING_GROUP_KEY].toObject();
@@ -557,7 +558,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer";
bool enableDynamicJitterBuffer = audioBufferGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool();
if (enableDynamicJitterBuffer) {
- qDebug() << "Enabling dynamic jitter buffers.";
+ qCDebug(audio) << "Enabling dynamic jitter buffers.";
bool ok;
const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames";
@@ -565,9 +566,9 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
if (!ok) {
_numStaticJitterFrames = InboundAudioStream::DEFAULT_STATIC_JITTER_FRAMES;
}
- qDebug() << "Static desired jitter buffer frames:" << _numStaticJitterFrames;
+ qCDebug(audio) << "Static desired jitter buffer frames:" << _numStaticJitterFrames;
} else {
- qDebug() << "Disabling dynamic jitter buffers.";
+ qCDebug(audio) << "Disabling dynamic jitter buffers.";
_numStaticJitterFrames = DISABLE_STATIC_JITTER_FRAMES;
}
@@ -621,7 +622,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
if (audioEnvGroupObject[CODEC_PREFERENCE_ORDER].isString()) {
QString codecPreferenceOrder = audioEnvGroupObject[CODEC_PREFERENCE_ORDER].toString();
_codecPreferenceOrder = codecPreferenceOrder.split(",");
- qDebug() << "Codec preference order changed to" << _codecPreferenceOrder;
+ qCDebug(audio) << "Codec preference order changed to" << _codecPreferenceOrder;
}
const QString ATTENATION_PER_DOULING_IN_DISTANCE = "attenuation_per_doubling_in_distance";
@@ -630,7 +631,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
float attenuation = audioEnvGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].toString().toFloat(&ok);
if (ok) {
_attenuationPerDoublingInDistance = attenuation;
- qDebug() << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance;
+ qCDebug(audio) << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance;
}
}
@@ -640,7 +641,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
float noiseMutingThreshold = audioEnvGroupObject[NOISE_MUTING_THRESHOLD].toString().toFloat(&ok);
if (ok) {
_noiseMutingThreshold = noiseMutingThreshold;
- qDebug() << "Noise muting threshold changed to" << _noiseMutingThreshold;
+ qCDebug(audio) << "Noise muting threshold changed to" << _noiseMutingThreshold;
}
}
@@ -680,8 +681,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
glm::vec3 dimensions(xMax - xMin, yMax - yMin, zMax - zMin);
AABox zoneAABox(corner, dimensions);
_audioZones.insert(zone, zoneAABox);
- qDebug() << "Added zone:" << zone << "(corner:" << corner
- << ", dimensions:" << dimensions << ")";
+ qCDebug(audio) << "Added zone:" << zone << "(corner:" << corner << ", dimensions:" << dimensions << ")";
}
}
}
@@ -712,7 +712,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
_audioZones.contains(settings.source) && _audioZones.contains(settings.listener)) {
_zoneSettings.push_back(settings);
- qDebug() << "Added Coefficient:" << settings.source << settings.listener << settings.coefficient;
+ qCDebug(audio) << "Added Coefficient:" << settings.source << settings.listener << settings.coefficient;
}
}
}
@@ -745,7 +745,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
_zoneReverbSettings.push_back(settings);
- qDebug() << "Added Reverb:" << zone << reverbTime << wetLevel;
+ qCDebug(audio) << "Added Reverb:" << zone << reverbTime << wetLevel;
}
}
}
diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp
index 9bba9c7f30..49453c6fc6 100644
--- a/assignment-client/src/audio/AudioMixerClientData.cpp
+++ b/assignment-client/src/audio/AudioMixerClientData.cpp
@@ -19,6 +19,7 @@
#include "InjectedAudioStream.h"
+#include "AudioLogging.h"
#include "AudioHelpers.h"
#include "AudioMixer.h"
#include "AudioMixerClientData.h"
@@ -132,7 +133,7 @@ void AudioMixerClientData::optionallyReplicatePacket(ReceivedMessage& message, c
if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) {
mirroredType = message.getType();
} else {
- qDebug() << "Packet passed to optionallyReplicatePacket was not a replicatable type - returning";
+ qCDebug(audio) << "Packet passed to optionallyReplicatePacket was not a replicatable type - returning";
return;
}
}
@@ -189,8 +190,16 @@ void AudioMixerClientData::parsePerAvatarGainSet(ReceivedMessage& message, const
uint8_t packedGain;
message.readPrimitive(&packedGain);
float gain = unpackFloatGainFromByte(packedGain);
- hrtfForStream(avatarUuid, QUuid()).setGainAdjustment(gain);
- qDebug() << "Setting gain adjustment for hrtf[" << uuid << "][" << avatarUuid << "] to " << gain;
+
+ if (avatarUuid.isNull()) {
+ // set the MASTER avatar gain
+ setMasterAvatarGain(gain);
+ qCDebug(audio) << "Setting MASTER avatar gain for " << uuid << " to " << gain;
+ } else {
+ // set the per-source avatar gain
+ hrtfForStream(avatarUuid, QUuid()).setGainAdjustment(gain);
+ qCDebug(audio) << "Setting avatar gain adjustment for hrtf[" << uuid << "][" << avatarUuid << "] to " << gain;
+ }
}
void AudioMixerClientData::parseNodeIgnoreRequest(QSharedPointer message, const SharedNodePointer& node) {
@@ -276,7 +285,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames());
avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO);
- qDebug() << "creating new AvatarAudioStream... codec:" << _selectedCodecName;
+ qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName;
connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec,
this, &AudioMixerClientData::handleMismatchAudioFormat);
@@ -315,7 +324,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
#if INJECTORS_SUPPORT_CODECS
injectorStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
- qDebug() << "creating new injectorStream... codec:" << _selectedCodecName;
+ qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName;
#endif
auto emplaced = _audioStreams.emplace(
@@ -339,8 +348,8 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
auto parseResult = matchingStream->parseData(message);
if (matchingStream->getOverflowCount() > overflowBefore) {
- qDebug() << "Just overflowed on stream from" << message.getSourceID() << "at" << message.getSenderSockAddr();
- qDebug() << "This stream is for" << (isMicStream ? "microphone audio" : "injected audio");
+ qCDebug(audio) << "Just overflowed on stream from" << message.getSourceID() << "at" << message.getSenderSockAddr();
+ qCDebug(audio) << "This stream is for" << (isMicStream ? "microphone audio" : "injected audio");
}
return parseResult;
@@ -689,7 +698,7 @@ void AudioMixerClientData::setupCodecForReplicatedAgent(QSharedPointerreadString();
if (codecString != _selectedCodecName) {
- qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())
+ qCDebug(audio) << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())
<< "-" << codecString;
const std::pair codec = AudioMixer::negotiateCodec({ codecString });
diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h
index 7a8690d8cc..c3a31715ea 100644
--- a/assignment-client/src/audio/AudioMixerClientData.h
+++ b/assignment-client/src/audio/AudioMixerClientData.h
@@ -83,6 +83,9 @@ public:
// uses randomization to have the AudioMixer send a stats packet to this node around every second
bool shouldSendStats(int frameNumber);
+ float getMasterAvatarGain() const { return _masterAvatarGain; }
+ void setMasterAvatarGain(float gain) { _masterAvatarGain = gain; }
+
AudioLimiter audioLimiter;
void setupCodec(CodecPluginPointer codec, const QString& codecName);
@@ -175,6 +178,8 @@ private:
int _frameToSendStats { 0 };
+ float _masterAvatarGain { 1.0f }; // per-listener mixing gain, applied only to avatars
+
CodecPluginPointer _codec;
QString _selectedCodecName;
Encoder* _encoder{ nullptr }; // for outbound mixed stream
diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp
index a131e266d2..6d150a0dc3 100644
--- a/assignment-client/src/audio/AudioMixerSlave.cpp
+++ b/assignment-client/src/audio/AudioMixerSlave.cpp
@@ -48,8 +48,8 @@ void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData&
// mix helpers
inline float approximateGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition);
-inline float computeGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
- const glm::vec3& relativePosition, bool isEcho);
+inline float computeGain(const AudioMixerClientData& listenerNodeData, const AvatarAudioStream& listeningNodeStream,
+ const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, bool isEcho);
inline float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition);
@@ -266,7 +266,7 @@ void AudioMixerSlave::addStream(AudioMixerClientData& listenerNodeData, const QU
glm::vec3 relativePosition = streamToAdd.getPosition() - listeningNodeStream.getPosition();
float distance = glm::max(glm::length(relativePosition), EPSILON);
- float gain = computeGain(listeningNodeStream, streamToAdd, relativePosition, isEcho);
+ float gain = computeGain(listenerNodeData, listeningNodeStream, streamToAdd, relativePosition, isEcho);
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
const int HRTF_DATASET_INDEX = 1;
@@ -484,10 +484,12 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi
// when throttling, as close streams are expected to be heard by a user
float distance = glm::length(relativePosition);
return gain / distance;
+
+ // avatar: skip master gain - it is constant for all streams
}
-float computeGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
- const glm::vec3& relativePosition, bool isEcho) {
+float computeGain(const AudioMixerClientData& listenerNodeData, const AvatarAudioStream& listeningNodeStream,
+ const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, bool isEcho) {
float gain = 1.0f;
// injector: apply attenuation
@@ -507,6 +509,9 @@ float computeGain(const AvatarAudioStream& listeningNodeStream, const Positional
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + (angleOfDelivery * (OFF_AXIS_ATTENUATION_STEP / PI_OVER_TWO));
gain *= offAxisCoefficient;
+
+ // apply master gain, only to avatars
+ gain *= listenerNodeData.getMasterAvatarGain();
}
auto& audioZones = AudioMixer::getAudioZones();
diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp
index fa9c73b12d..995a5bad27 100644
--- a/assignment-client/src/entities/EntityServer.cpp
+++ b/assignment-client/src/entities/EntityServer.cpp
@@ -41,8 +41,15 @@ EntityServer::EntityServer(ReceivedMessage& message) :
DependencyManager::set();
auto& packetReceiver = DependencyManager::get()->getPacketReceiver();
- packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics, PacketType::ChallengeOwnership },
- this, "handleEntityPacket");
+ packetReceiver.registerListenerForTypes({ PacketType::EntityAdd,
+ PacketType::EntityEdit,
+ PacketType::EntityErase,
+ PacketType::EntityPhysics,
+ PacketType::ChallengeOwnership,
+ PacketType::ChallengeOwnershipRequest,
+ PacketType::ChallengeOwnershipReply },
+ this,
+ "handleEntityPacket");
connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification);
_dynamicDomainVerificationTimer.setSingleShot(true);
@@ -459,7 +466,7 @@ void EntityServer::startDynamicDomainVerification() {
EntityItemPointer entity = tree->findEntityByEntityItemID(i.value());
if (entity) {
- if (!entity->verifyStaticCertificateProperties()) {
+ if (!entity->getProperties().verifyStaticCertificateProperties()) {
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed"
<< "static certificate verification.";
// Delete the entity if it doesn't pass static certificate verification
diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp
index 03014bae6a..e5cee84f1b 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.cpp
+++ b/assignment-client/src/entities/EntityTreeSendThread.cpp
@@ -23,6 +23,17 @@ EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedN
{
connect(std::static_pointer_cast(myServer->getOctree()).get(), &EntityTree::editingEntityPointer, this, &EntityTreeSendThread::editingEntityPointer, Qt::QueuedConnection);
connect(std::static_pointer_cast(myServer->getOctree()).get(), &EntityTree::deletingEntityPointer, this, &EntityTreeSendThread::deletingEntityPointer, Qt::QueuedConnection);
+
+ // connect to connection ID change on EntityNodeData so we can clear state for this receiver
+ auto nodeData = static_cast(node->getLinkedData());
+ connect(nodeData, &EntityNodeData::incomingConnectionIDChanged, this, &EntityTreeSendThread::resetState);
+}
+
+void EntityTreeSendThread::resetState() {
+ qCDebug(entities) << "Clearing known EntityTreeSendThread state for" << _nodeUuid;
+
+ _knownState.clear();
+ _traversal.reset();
}
void EntityTreeSendThread::preDistributionProcessing() {
@@ -175,7 +186,7 @@ bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filte
return parentWasNew || ancestorsWereNew;
}
- // since we didn't have a parent niether of our parents or ancestors could be new additions
+ // since we didn't have a parent, neither of our parents or ancestors could be new additions
return false;
}
@@ -204,7 +215,9 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
return hasNewChild || hasNewDescendants;
}
-void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum) {
+void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset,
+ bool usesViewFrustum) {
+
DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesViewFrustum);
// there are three types of traversal:
//
@@ -423,12 +436,19 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
uint64_t sendTime = usecTimestampNow();
auto nodeData = static_cast(params.nodeData);
nodeData->stats.encodeStarted();
+ auto entityNode = _node.toStrongRef();
+ auto entityNodeData = static_cast(entityNode->getLinkedData());
while(!_sendQueue.empty()) {
PrioritizedEntity queuedItem = _sendQueue.top();
EntityItemPointer entity = queuedItem.getEntity();
if (entity) {
// Only send entities that match the jsonFilters, but keep track of everything we've tried to send so we don't try to send it again
- if (entity->matchesJSONFilters(jsonFilters)) {
+ bool entityMatchesFilters = entity->matchesJSONFilters(jsonFilters);
+ if (entityMatchesFilters || entityNodeData->isEntityFlaggedAsExtra(entity->getID())) {
+ if (!jsonFilters.isEmpty() && entityMatchesFilters) {
+ // Record explicitly filtered-in entity so that extra entities can be flagged.
+ entityNodeData->insertSentFilteredEntity(entity->getID());
+ }
OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData);
if (appendEntityState != OctreeElement::COMPLETED) {
diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h
index 49901491ff..594f423838 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.h
+++ b/assignment-client/src/entities/EntityTreeSendThread.h
@@ -33,12 +33,16 @@ protected:
void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene) override;
+private slots:
+ void resetState(); // clears our known state forcing entities to appear unsent
+
private:
// the following two methods return booleans to indicate if any extra flagged entities were new additions to set
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
- void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum);
+ void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset,
+ bool usesViewFrustum);
bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
void preDistributionProcessing() override;
diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp
index 3f835678ac..bce6e7fe44 100644
--- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp
+++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp
@@ -96,6 +96,14 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer
_myServer->getOctree()->withWriteLock([&] {
_myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode);
});
+ } else if (packetType == PacketType::ChallengeOwnershipRequest) {
+ _myServer->getOctree()->withWriteLock([&] {
+ _myServer->getOctree()->processChallengeOwnershipRequestPacket(*message, sendingNode);
+ });
+ } else if (packetType == PacketType::ChallengeOwnershipReply) {
+ _myServer->getOctree()->withWriteLock([&] {
+ _myServer->getOctree()->processChallengeOwnershipReplyPacket(*message, sendingNode);
+ });
} else if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket);
_receivedPacketCount++;
diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp
index 89e3d403fc..3ae653307f 100644
--- a/assignment-client/src/octree/OctreeSendThread.cpp
+++ b/assignment-client/src/octree/OctreeSendThread.cpp
@@ -82,8 +82,12 @@ bool OctreeSendThread::process() {
if (auto node = _node.lock()) {
OctreeQueryNode* nodeData = static_cast(node->getLinkedData());
- // Sometimes the node data has not yet been linked, in which case we can't really do anything
- if (nodeData && !nodeData->isShuttingDown()) {
+ // If we don't have the OctreeQueryNode at all
+ // or it's uninitialized because we haven't received a query yet from the client
+ // or we don't know where we should send packets for this node
+ // or we're shutting down
+ // then we can't send an entity data packet
+ if (nodeData && nodeData->hasReceivedFirstQuery() && node->getActiveSocket() && !nodeData->isShuttingDown()) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
packetDistributor(node, nodeData, viewFrustumChanged);
}
diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h
index bc7d2c2588..220952e209 100644
--- a/assignment-client/src/octree/OctreeSendThread.h
+++ b/assignment-client/src/octree/OctreeSendThread.h
@@ -59,7 +59,8 @@ protected:
OctreePacketData _packetData;
QWeakPointer _node;
OctreeServer* _myServer { nullptr };
-
+ QUuid _nodeUuid;
+
private:
/// Called before a packetDistributor pass to allow for pre-distribution processing
virtual void preDistributionProcessing() {};
@@ -71,8 +72,6 @@ private:
virtual void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene);
virtual bool shouldTraverseAndSend(OctreeQueryNode* nodeData) { return hasSomethingToSend(nodeData); }
- QUuid _nodeUuid;
-
int _truePacketsSent { 0 }; // available for debug stats
int _trueBytesSent { 0 }; // available for debug stats
int _packetsSentThisInterval { 0 }; // used for bandwidth throttle condition
diff --git a/cmake/externals/hifiAudioCodec/CMakeLists.txt b/cmake/externals/hifiAudioCodec/CMakeLists.txt
index a30396c6fd..e3ba36a440 100644
--- a/cmake/externals/hifiAudioCodec/CMakeLists.txt
+++ b/cmake/externals/hifiAudioCodec/CMakeLists.txt
@@ -5,43 +5,41 @@ set(EXTERNAL_NAME hifiAudioCodec)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
-if (NOT ANDROID)
-
- if (WIN32 OR APPLE)
- ExternalProject_Add(
- ${EXTERNAL_NAME}
- URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-1.zip
- URL_MD5 23ec3fe51eaa155ea159a4971856fc13
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- LOG_DOWNLOAD 1
- )
- else ()
- ExternalProject_Add(
- ${EXTERNAL_NAME}
- URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux.zip
- URL_MD5 7d37914a18aa4de971d2f45dd3043bde
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- LOG_DOWNLOAD 1
- )
- endif()
-
- # Hide this external target (for ide users)
- set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
-
- ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
-
- set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
-
- if (WIN32)
- set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL)
- elseif(APPLE)
- set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
- elseif(NOT ANDROID)
- set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
- endif()
-
+if (WIN32)
+ set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-win-2.0.zip)
+ set(DOWNLOAD_MD5 9199d4dbd6b16bed736b235efe980e67)
+elseif (APPLE)
+ set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-mac-2.0.zip)
+ set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba)
+elseif (ANDROID)
+ set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip)
+ set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683)
+elseif (UNIX)
+ set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip)
+ set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481)
+else()
+ return()
+endif()
+
+ExternalProject_Add(
+ ${EXTERNAL_NAME}
+ URL ${DOWNLOAD_URL}
+ URL_MD5 ${DOWNLOAD_MD5}
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ LOG_DOWNLOAD 1
+)
+
+# Hide this external target (for ide users)
+set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
+
+ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
+
+set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
+
+if (WIN32)
+ set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL)
+else()
+ set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
endif()
diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt
index 4437024962..4c0ffaf88f 100644
--- a/cmake/externals/wasapi/CMakeLists.txt
+++ b/cmake/externals/wasapi/CMakeLists.txt
@@ -6,8 +6,8 @@ if (WIN32)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
- URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip
- URL_MD5 94f4765bdbcd53cd099f349ae031e769
+ URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi10.zip
+ URL_MD5 4f40e49715a420fb67b45b9cee19052c
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake
index 338dee7bc8..0619c4d587 100644
--- a/cmake/modules/FindOpenSSL.cmake
+++ b/cmake/modules/FindOpenSSL.cmake
@@ -60,7 +60,7 @@ if (WIN32 AND NOT CYGWIN)
select_library_configurations(LIB_EAY)
select_library_configurations(SSL_EAY)
set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY})
- find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" ${_OPENSSL_ROOT_HINTS_AND_PATHS})
+ find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} NO_DEFAULT_PATH)
endif()
else()
diff --git a/domain-server/resources/web/content/index.shtml b/domain-server/resources/web/content/index.shtml
index e1ba5499b6..0e48c1eff8 100644
--- a/domain-server/resources/web/content/index.shtml
+++ b/domain-server/resources/web/content/index.shtml
@@ -19,12 +19,13 @@
Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.
Note: Your domain's content will be replaced by the content you upload, but the backup files of your domain's content will not immediately be changed.
-
- If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:
-
If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:
diff --git a/domain-server/resources/web/js/shared.js b/domain-server/resources/web/js/shared.js
index a5dd522a53..00f699fa4e 100644
--- a/domain-server/resources/web/js/shared.js
+++ b/domain-server/resources/web/js/shared.js
@@ -62,26 +62,25 @@ var Strings = {
// dialog with new path still set, allowing them to retry immediately, and without
// having to type the new path in again.
EDIT_PLACE_TITLE: "Modify Viewpoint or Path",
- EDIT_PLACE_ERROR: "Failed to update place path. Please try again.",
+ EDIT_PLACE_ERROR: "Failed to update Viewpoint or Path for this Place Name. Please try again.",
EDIT_PLACE_CONFIRM_BUTTON: "Save",
EDIT_PLACE_CONFIRM_BUTTON_PENDING: "Saving...",
EDIT_PLACE_CANCEL_BUTTON: "Cancel",
- REMOVE_PLACE_TITLE: "Are you sure you want to remove {{place}}?",
- REMOVE_PLACE_ERROR: "Failed to remove place. Please try again.",
- REMOVE_PLACE_DELETE_BUTTON: "Delete",
+ REMOVE_PLACE_TITLE: "Are you sure you want to remove {{place}} and its path information?",
+ REMOVE_PLACE_ERROR: "Failed to remove Place Name and its Path information.",
+ REMOVE_PLACE_DELETE_BUTTON: "This action removes your Place Name",
REMOVE_PLACE_DELETE_BUTTON_PENDING: "Deleting...",
REMOVE_PLACE_CANCEL_BUTTON: "Cancel",
ADD_PLACE_TITLE: "Choose a place",
- ADD_PLACE_MESSAGE: "Choose the High Fidelity place to point at this domain server.",
- ADD_PLACE_CONFIRM_BUTTON: "Choose place",
+ ADD_PLACE_MESSAGE: "Choose a Place Name that you own or register a new Place Name.",
+ ADD_PLACE_CONFIRM_BUTTON: "Save",
ADD_PLACE_CONFIRM_BUTTON_PENDING: "Saving...",
ADD_PLACE_CANCEL_BUTTON: "Cancel",
- ADD_PLACE_UNKNOWN_ERROR: "There was an error adding this place name.",
+ ADD_PLACE_UNKNOWN_ERROR: "There was an error adding this Place Name. Try saving again",
- ADD_PLACE_NO_PLACES_MESSAGE: "
You do not have any places in your High Fidelity account."
- + "
Go to your places page to create a new one. Once your place is created re-open this dialog to select it.
",
+ ADD_PLACE_NO_PLACES_MESSAGE: "You don't have any Place Names registered. Once you have a Place Name, reopen this window to select it.",
ADD_PLACE_NO_PLACES_BUTTON: "Create new place",
ADD_PLACE_UNABLE_TO_LOAD_ERROR: "We were unable to load your place names. Please try again later.",
ADD_PLACE_LOADING_DIALOG: "Loading your places...",
@@ -146,187 +145,256 @@ function sendUpdatePlaceRequest(id, path, domainID, clearDomainID, onSuccess, on
});
}
+var pendingDomainRequest = null;
+function getDomainFromAPI(callback) {
+ if (pendingDomainRequest !== null) {
+ pendingDomainRequest.success(callback);
+ pendingDomainRequest.error(function() { callback({ status: 'fail' }) });
+ return pendingDomainRequest;
+ }
+
+ if (callback === undefined) {
+ callback = function() {};
+ }
+
+ var domainID = Settings.data.values.metaverse.id;
+ if (domainID === null || domainID === undefined || domainID === '') {
+ callback({ status: 'fail' });
+ return null;
+ }
+
+ pendingDomainRequest = $.ajax({
+ url: "/api/domains/" + domainID,
+ dataType: 'json',
+ success: function(data) {
+ pendingDomainRequest = null;
+
+ if (data.status === 'success') {
+ DomainInfo = data.domain;
+ } else {
+ DomainInfo = null;
+ }
+ callback(data);
+ },
+ error: function() {
+ pendingDomainRequest = null;
+
+ DomainInfo = null;
+ callback({ status: 'fail' });
+ }
+ });
+
+ return pendingDomainRequest;
+}
+
function chooseFromHighFidelityPlaces(accessToken, forcePathTo, onSuccessfullyAdded) {
if (accessToken) {
var loadingDialog = showLoadingDialog(Strings.ADD_PLACE_LOADING_DIALOG);
- $.ajax("/api/places", {
- dataType: 'json',
- jsonp: false,
- success: function(data) {
- if (data.status == 'success') {
- var modal_buttons = {
- cancel: {
- label: Strings.ADD_PLACE_CANCEL_BUTTON,
- className: 'add-place-cancel-button btn-default'
- }
- };
-
- var dialog;
- var modal_body;
-
- if (data.data.places.length) {
- var places_by_id = {};
-
- modal_body = $('
diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js
index 1224b724da..3faeff4482 100644
--- a/domain-server/resources/web/settings/js/settings.js
+++ b/domain-server/resources/web/settings/js/settings.js
@@ -503,7 +503,7 @@ function showDomainCreationAlert(justConnected) {
swal({
title: 'Create new domain ID',
type: 'input',
- text: 'Enter a short description for this machine.This will help you identify which domain ID belongs to which machine.',
+ text: 'Enter a label this machine.This will help you identify which domain ID belongs to which machine.',
showCancelButton: true,
confirmButtonText: "Create",
closeOnConfirm: false,
@@ -527,13 +527,12 @@ function showDomainCreationAlert(justConnected) {
function createNewDomainID(label, justConnected) {
// get the JSON object ready that we'll use to create a new domain
var domainJSON = {
- "label": label
- //"access_token": $(Settings.ACCESS_TOKEN_SELECTOR).val()
+ "label": label
}
$.post("/api/domains", domainJSON, function(data){
// we successfully created a domain ID, set it on that field
- var domainID = data.domain_id;
+ var domainID = data.domain.id;
console.log("Setting domain id to ", data, domainID);
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
@@ -620,18 +619,14 @@ function parseJSONResponse(xhr) {
function showOrHideLabel() {
var type = getCurrentDomainIDType();
- if (!accessTokenIsSet() || (type !== DOMAIN_ID_TYPE_FULL && type !== DOMAIN_ID_TYPE_UNKNOWN)) {
- $(".panel#label").hide();
- return false;
- }
- $(".panel#label").show();
- return true;
+ var shouldShow = accessTokenIsSet() && (type === DOMAIN_ID_TYPE_FULL || type === DOMAIN_ID_TYPE_UNKNOWN);
+ $(".panel#label").toggle(shouldShow);
+ $("li a[href='#label']").parent().toggle(shouldShow);
+ return shouldShow;
}
function setupDomainLabelSetting() {
- if (!showOrHideLabel()) {
- return;
- }
+ showOrHideLabel();
var html = "
- Add your High Fidelity username and any other usernames to grant administrator privileges.
-
+ Add your High Fidelity username and any other usernames to grant administrator privileges
@@ -78,7 +77,7 @@
- Who can connect to your domain?
+ Who can connect to your domain?
@@ -87,25 +86,21 @@
@@ -113,7 +108,7 @@
- Who can rez items in your domain?
+ Who can rez items in your domain?
@@ -122,33 +117,32 @@
-
+
+
+
+
-
+
@@ -188,35 +182,37 @@
-
+
+
+
+
-
+
-
+
-
-
+
+
Congratulations! You have successfully setup and configured your cloud hosted domain.
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/domain-server/resources/web/wizard/js/wizard.js b/domain-server/resources/web/wizard/js/wizard.js
index 24a82402a6..57e85973f4 100644
--- a/domain-server/resources/web/wizard/js/wizard.js
+++ b/domain-server/resources/web/wizard/js/wizard.js
@@ -2,6 +2,8 @@ var Metaverse = {
accessToken: null
}
+var currentStepNumber;
+
$(document).ready(function(){
Strings.ADD_PLACE_NOT_CONNECTED_MESSAGE = "You must have an access token to query your High Fidelity places.
" +
"Please go back and connect your account.";
@@ -9,6 +11,22 @@ $(document).ready(function(){
$('#connect-account-btn').attr('href', URLs.METAVERSE_URL + "/user/tokens/new?for_domain_server=true");
$('[data-toggle="tooltip"]').tooltip();
+
+ $('.perms-link').on('click', function() {
+ var modal_body = '
';
+ modal_body += 'None - No one will have permissions. Only you and the users your have given administrator privileges to will have permissions.';
+ modal_body += 'Friends - Users who are your Friends in High Fidelity.';
+ modal_body += 'Users logged into High Fidelity - Users who are currently logged into High Fidelity.';
+ modal_body += 'Everyone - Anyone who uses High Fidelity.';
+ modal_body += '
';
+
+ dialog = bootbox.dialog({
+ title: "User definition",
+ message: modal_body,
+ closeButton: true
+ });
+ return false;
+ });
$('body').on('click', '.next-button', function() {
goToNextStep();
@@ -56,8 +74,39 @@ $(document).ready(function(){
exploreSettings();
});
+ $('input[type=radio][name=connect-radio]').change(function() {
+ var inputs = $('input[type=radio][name=rez-radio]');
+ var disabled = [];
+
+ switch (this.value) {
+ case 'none':
+ disabled = inputs.splice(1);
+ break;
+ case 'friends':
+ disabled = inputs.splice(2);
+ break;
+ case 'logged-in':
+ disabled = inputs.splice(3);
+ break;
+ case 'everyone':
+ disabled = inputs.splice(4);
+ break;
+ }
+
+ $.each(inputs, function() {
+ $(this).prop('disabled', false);
+ });
+ $.each(disabled, function() {
+ if ($(this).prop('checked')) {
+ $(inputs.last()).prop('checked', true);
+ }
+ $(this).prop('disabled', true);
+ });
+ });
+
reloadSettings(function(success) {
if (success) {
+ getDomainFromAPI();
setupWizardSteps();
updatePlaceNameDisplay();
updateUsernameDisplay();
@@ -72,12 +121,12 @@ $(document).ready(function(){
});
function setupWizardSteps() {
- var stepsCompleted = Settings.data.values.wizard.steps_completed;
+ currentStepNumber = Settings.data.values.wizard.steps_completed;
var steps = null;
if (Settings.data.values.wizard.cloud_domain) {
$('.desktop-only').remove();
- $('.wizard-step').find('.back-button').hide();
+ $('.wizard-step:first').find('.back-button').hide();
steps = $('.wizard-step');
$(steps).each(function(i) {
@@ -85,7 +134,7 @@ function setupWizardSteps() {
});
$('#permissions-description').html('You have been assigned administrator privileges to this domain.');
- $('#admin-description').html('Add more High Fidelity usernames to grant administrator privileges.');
+ $('#admin-description').html('Add more High Fidelity usernames');
} else {
$('.cloud-only').remove();
$('#save-permissions').text("Finish");
@@ -95,12 +144,12 @@ function setupWizardSteps() {
$(this).children(".step-title").text("Step " + (i + 1) + " of " + steps.length);
});
- if (stepsCompleted == 0) {
+ if (currentStepNumber == 0) {
$('#skip-wizard-button').show();
}
}
- var currentStep = steps[stepsCompleted];
+ var currentStep = steps[currentStepNumber];
$(currentStep).show();
}
@@ -203,7 +252,7 @@ function goToNextStep() {
currentStep.hide();
nextStep.show();
- var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
+ currentStepNumber += 1;
postSettings({
"wizard": {
@@ -232,7 +281,7 @@ function goToPreviousStep() {
currentStep.hide();
previousStep.show();
- var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) - 1;
+ currentStepNumber -= 1;
postSettings({
"wizard": {
@@ -438,7 +487,7 @@ function saveUsernamePassword() {
return;
}
- var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
+ currentStepNumber += 1;
var formJSON = {
"security": {
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 7b0b709bf7..c2fe3af7c1 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -830,26 +830,6 @@ void DomainServer::setupICEHeartbeatForFullNetworking() {
void DomainServer::updateICEServerAddresses() {
if (_iceAddressLookupID == INVALID_ICE_LOOKUP_ID) {
_iceAddressLookupID = QHostInfo::lookupHost(_iceServerAddr, this, SLOT(handleICEHostInfo(QHostInfo)));
-
- // there seems to be a 5.9 bug where lookupHost never calls our slot
- // so we add a single shot manual "timeout" to fire it off again if it hasn't called back yet
- static const int ICE_ADDRESS_LOOKUP_TIMEOUT_MS = 5000;
- QTimer::singleShot(ICE_ADDRESS_LOOKUP_TIMEOUT_MS, this, &DomainServer::timeoutICEAddressLookup);
- }
-}
-
-void DomainServer::timeoutICEAddressLookup() {
- if (_iceAddressLookupID != INVALID_ICE_LOOKUP_ID) {
- // we waited 5s and didn't hear back for our ICE DNS lookup
- // so time that one out and kick off another
-
- qDebug() << "IP address lookup timed out for" << _iceServerAddr << "- retrying";
-
- QHostInfo::abortHostLookup(_iceAddressLookupID);
-
- _iceAddressLookupID = INVALID_ICE_LOOKUP_ID;
-
- updateICEServerAddresses();
}
}
@@ -3007,9 +2987,20 @@ void DomainServer::handleKeypairChange() {
void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
// clear the ICE address lookup ID so that it can fire again
- _iceAddressLookupID = -1;
+ _iceAddressLookupID = INVALID_ICE_LOOKUP_ID;
- if (hostInfo.error() != QHostInfo::NoError) {
+ // enumerate the returned addresses and collect only valid IPv4 addresses
+ QList sanitizedAddresses = hostInfo.addresses();
+ auto it = sanitizedAddresses.begin();
+ while (it != sanitizedAddresses.end()) {
+ if (!it->isNull() && it->protocol() == QAbstractSocket::IPv4Protocol) {
+ ++it;
+ } else {
+ it = sanitizedAddresses.erase(it);
+ }
+ }
+
+ if (hostInfo.error() != QHostInfo::NoError || sanitizedAddresses.empty()) {
qWarning() << "IP address lookup failed for" << _iceServerAddr << ":" << hostInfo.errorString();
// if we don't have an ICE server to use yet, trigger a retry
@@ -3022,7 +3013,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
} else {
int countBefore = _iceServerAddresses.count();
- _iceServerAddresses = hostInfo.addresses();
+ _iceServerAddresses = sanitizedAddresses;
if (countBefore == 0) {
qInfo() << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << _iceServerAddr;
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index 3fc87bbd59..b45b8a4816 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -116,8 +116,6 @@ private slots:
void tokenGrantFinished();
void profileRequestFinished();
- void timeoutICEAddressLookup();
-
signals:
void iceServerChanged();
void userConnected();
diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp
index 43262a1a81..6c50e5245d 100644
--- a/domain-server/src/DomainServerSettingsManager.cpp
+++ b/domain-server/src/DomainServerSettingsManager.cpp
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include //for KillAvatarReason
#include
#include "DomainServerNodeData.h"
@@ -43,12 +44,7 @@ const QString DESCRIPTION_COLUMNS_KEY = "columns";
const QString SETTINGS_VIEWPOINT_KEY = "viewpoint";
-static Setting::Handle JSON_SETTING_VERSION("json-settings/version", 0.0);
-
-DomainServerSettingsManager::DomainServerSettingsManager() :
- _descriptionArray(),
- _configMap()
-{
+DomainServerSettingsManager::DomainServerSettingsManager() {
// load the description object from the settings description
QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH);
descriptionFile.open(QIODevice::ReadOnly);
@@ -100,12 +96,34 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer JSON_SETTING_VERSION("json-settings/version", 0.0);
+ if (JSON_SETTING_VERSION.isSet()) {
+ auto version = JSON_SETTING_VERSION.get();
+ *versionVariant = version;
+ persistToFile();
+ QFile::remove(settingsFilename());
+ }
+ }
// What settings version were we before and what are we using now?
// Do we need to do any re-mapping?
- double oldVersion = JSON_SETTING_VERSION.get();
+ double oldVersion = versionVariant->toDouble();
if (oldVersion != _descriptionVersion) {
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
@@ -137,12 +155,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
QVariant* restrictedAccess = _configMap.valueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH, true);
*restrictedAccess = QVariant(true);
-
- // write the new settings to the json file
- persistToFile();
-
- // reload the master and user config so that the merged config is right
- _configMap.loadMasterAndUserConfig(_argumentList);
}
}
@@ -172,12 +184,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
*entityServerVariant = entityServerMap;
}
-
- // write the new settings to the json file
- persistToFile();
-
- // reload the master and user config so that the merged config is right
- _configMap.loadMasterAndUserConfig(_argumentList);
}
}
@@ -195,12 +201,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
qDebug() << "Migrating plaintext password to SHA256 hash in domain-server settings.";
*passwordVariant = QCryptographicHash::hash(plaintextPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
-
- // write the new settings to file
- persistToFile();
-
- // reload the master and user config so the merged config is correct
- _configMap.loadMasterAndUserConfig(_argumentList);
}
}
@@ -283,19 +283,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
packPermissions();
}
- if (oldVersion < 1.7) {
- // This was prior to the removal of the master config file
- // So we write the merged config to the user config file, and stop reading from the user config file
-
- qDebug() << "Migrating merged config to user config file. The master config file is deprecated.";
-
- // replace the user config by the merged config
- _configMap.getConfig() = _configMap.getMergedConfig();
-
- // persist the new config so the user config file has the correctly merged config
- persistToFile();
- }
-
if (oldVersion < 1.8) {
unpackPermissions();
// This was prior to addition of domain content replacement, add that to localhost permissions by default
@@ -316,16 +303,16 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
QVariant* wizardCompletedOnce = _configMap.valueForKeyPath(WIZARD_COMPLETED_ONCE, true);
*wizardCompletedOnce = QVariant(true);
-
- // write the new settings to the json file
- persistToFile();
}
+
+ // write the current description version to our settings
+ *versionVariant = _descriptionVersion;
+
+ // write the new settings to the json file
+ persistToFile();
}
unpackPermissions();
-
- // write the current description version to our settings
- JSON_SETTING_VERSION.set(_descriptionVersion);
}
QVariantMap& DomainServerSettingsManager::getDescriptorsMap() {
@@ -1289,9 +1276,6 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
}
}
- // re-merge the user and master configs after a settings change
- _configMap.mergeMasterAndUserConfigs();
-
return needRestart;
}
diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf
index 3db48602b1..4cc5a0fe4f 100644
Binary files a/interface/resources/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs.ttf differ
diff --git a/interface/resources/images/lowerKeyboard.png b/interface/resources/images/lowerKeyboard.png
new file mode 100644
index 0000000000..d379b028ab
Binary files /dev/null and b/interface/resources/images/lowerKeyboard.png differ
diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml
deleted file mode 100644
index 60d2bacc62..0000000000
--- a/interface/resources/qml/AddressBarDialog.qml
+++ /dev/null
@@ -1,532 +0,0 @@
-//
-// AddressBarDialog.qml
-//
-// Created by Austin Davis on 2015/04/14
-// Copyright 2015 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-import Hifi 1.0
-import QtQuick 2.4
-import "controls"
-import "styles"
-import "windows"
-import "hifi"
-import "hifi/toolbars"
-import "styles-uit" as HifiStyles
-import "controls-uit" as HifiControls
-
-Window {
- id: root
- HifiConstants { id: hifi }
- HifiStyles.HifiConstants { id: hifiStyleConstants }
-
- objectName: "AddressBarDialog"
- title: "Go To:"
-
- shown: false
- destroyOnHidden: false
- resizable: false
- pinnable: false;
-
- width: addressBarDialog.implicitWidth
- height: addressBarDialog.implicitHeight
- property int gap: 14
-
- onShownChanged: {
- addressBarDialog.keyboardEnabled = HMD.active;
- addressBarDialog.observeShownChanged(shown);
- }
- Component.onCompleted: {
- root.parentChanged.connect(center);
- center();
- }
- Component.onDestruction: {
- root.parentChanged.disconnect(center);
- }
-
- function center() {
- // Explicitly center in order to avoid warnings at shutdown
- anchors.centerIn = parent;
- }
-
- function resetAfterTeleport() {
- storyCardFrame.shown = root.shown = false;
- }
- function goCard(targetString) {
- if (0 !== targetString.indexOf('hifi://')) {
- storyCardHTML.url = addressBarDialog.metaverseServerUrl + targetString;
- storyCardFrame.shown = true;
- return;
- }
- addressLine.text = targetString;
- toggleOrGo(true);
- clearAddressLineTimer.start();
- }
- property var allStories: [];
- property int cardWidth: 212;
- property int cardHeight: 152;
- property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
- property bool isCursorVisible: false // Override default cursor visibility.
-
- AddressBarDialog {
- id: addressBarDialog
-
- property bool keyboardEnabled: false
- property bool keyboardRaised: false
- property bool punctuationMode: false
-
- implicitWidth: backgroundImage.width
- implicitHeight: scroll.height + gap + backgroundImage.height + (keyboardEnabled ? keyboard.height : 0);
-
- // The buttons have their button state changed on hover, so we have to manually fix them up here
- onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
- onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0;
- onReceivedHifiSchemeURL: resetAfterTeleport();
-
- // Update location after using back and forward buttons.
- onHostChanged: updateLocationTextTimer.start();
-
- ListModel { id: suggestions }
-
- ListView {
- id: scroll
- height: cardHeight + scroll.stackedCardShadowHeight
- property int stackedCardShadowHeight: 10;
- spacing: gap;
- clip: true;
- anchors {
- left: backgroundImage.left
- right: swipe.left
- bottom: backgroundImage.top
- }
- model: suggestions;
- orientation: ListView.Horizontal;
- delegate: Card {
- width: cardWidth;
- height: cardHeight;
- goFunction: goCard;
- userName: model.username;
- placeName: model.place_name;
- hifiUrl: model.place_name + model.path;
- thumbnail: model.thumbnail_url;
- imageUrl: model.image_url;
- action: model.action;
- timestamp: model.created_at;
- onlineUsers: model.online_users;
- storyId: model.metaverseId;
- drillDownToPlace: model.drillDownToPlace;
- shadowHeight: scroll.stackedCardShadowHeight;
- hoverThunk: function () { ListView.view.currentIndex = index; }
- unhoverThunk: function () { ListView.view.currentIndex = -1; }
- }
- highlightMoveDuration: -1;
- highlightMoveVelocity: -1;
- highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; }
- }
- Image { // Just a visual indicator that the user can swipe the cards over to see more.
- id: swipe;
- source: "../images/swipe-chevron.svg";
- width: 72;
- visible: suggestions.count > 3;
- anchors {
- right: backgroundImage.right;
- top: scroll.top;
- }
- MouseArea {
- anchors.fill: parent
- onClicked: scroll.currentIndex = (scroll.currentIndex < 0) ? 3 : (scroll.currentIndex + 3)
- }
- }
-
- Row {
- spacing: 2 * hifi.layout.spacing;
- anchors {
- top: parent.top;
- left: parent.left;
- leftMargin: 150;
- topMargin: -30;
- }
- property var selected: allTab;
- TextButton {
- id: allTab;
- text: "ALL";
- property string includeActions: 'snapshot,concurrency';
- selected: allTab === selectedTab;
- action: tabSelect;
- }
- TextButton {
- id: placeTab;
- text: "PLACES";
- property string includeActions: 'concurrency';
- selected: placeTab === selectedTab;
- action: tabSelect;
- }
- TextButton {
- id: snapsTab;
- text: "SNAPS";
- property string includeActions: 'snapshot';
- selected: snapsTab === selectedTab;
- action: tabSelect;
- }
- }
-
- Image {
- id: backgroundImage
- source: "../images/address-bar-856.svg"
- width: 856
- height: 100
- anchors {
- bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom;
- }
- property int inputAreaHeight: 70
- property int inputAreaStep: (height - inputAreaHeight) / 2
-
- ToolbarButton {
- id: homeButton
- imageURL: "../images/home.svg"
- onClicked: {
- addressBarDialog.loadHome();
- root.shown = false;
- }
- anchors {
- left: parent.left
- leftMargin: homeButton.width / 2
- verticalCenter: parent.verticalCenter
- }
- }
-
- ToolbarButton {
- id: backArrow;
- imageURL: "../images/backward.svg";
- onClicked: addressBarDialog.loadBack();
- anchors {
- left: homeButton.right
- verticalCenter: parent.verticalCenter
- }
- }
- ToolbarButton {
- id: forwardArrow;
- imageURL: "../images/forward.svg";
- onClicked: addressBarDialog.loadForward();
- anchors {
- left: backArrow.right
- verticalCenter: parent.verticalCenter
- }
- }
-
- HifiStyles.RalewayLight {
- id: notice;
- font.pixelSize: hifi.fonts.pixelSize * 0.50;
- anchors {
- top: parent.top
- topMargin: parent.inputAreaStep + 12
- left: addressLine.left
- right: addressLine.right
- }
- }
- HifiStyles.FiraSansRegular {
- id: location;
- font.pixelSize: addressLine.font.pixelSize;
- color: "gray";
- clip: true;
- anchors.fill: addressLine;
- visible: addressLine.text.length === 0
- }
- TextInput {
- id: addressLine
- focus: true
- anchors {
- top: parent.top
- bottom: parent.bottom
- left: forwardArrow.right
- right: parent.right
- leftMargin: forwardArrow.width
- rightMargin: forwardArrow.width / 2
- topMargin: parent.inputAreaStep + (2 * hifi.layout.spacing)
- bottomMargin: parent.inputAreaStep
- }
- font.pixelSize: hifi.fonts.pixelSize * 0.75
- cursorVisible: false
- onTextChanged: {
- filterChoicesByText();
- updateLocationText(text.length > 0);
- if (!isCursorVisible && text.length > 0) {
- isCursorVisible = true;
- cursorVisible = true;
- }
- }
- onActiveFocusChanged: {
- cursorVisible = isCursorVisible && focus;
- }
- MouseArea {
- // If user clicks in address bar show cursor to indicate ability to enter address.
- anchors.fill: parent
- onClicked: {
- isCursorVisible = true;
- parent.cursorVisible = true;
- parent.forceActiveFocus();
- }
- }
- }
- }
-
- Timer {
- // Delay updating location text a bit to avoid flicker of content and so that connection status is valid.
- id: updateLocationTextTimer
- running: false
- interval: 500 // ms
- repeat: false
- onTriggered: updateLocationText(false);
- }
-
- Timer {
- // Delay clearing address line so as to avoid flicker of "not connected" being displayed after entering an address.
- id: clearAddressLineTimer
- running: false
- interval: 100 // ms
- repeat: false
- onTriggered: {
- addressLine.text = "";
- isCursorVisible = false;
- }
- }
-
- Window {
- width: 938
- height: 625
- HifiControls.WebView {
- anchors.fill: parent;
- id: storyCardHTML;
- }
- id: storyCardFrame;
-
- shown: false;
- destroyOnCloseButton: false;
- pinnable: false;
-
- anchors {
- verticalCenter: backgroundImage.verticalCenter;
- horizontalCenter: scroll.horizontalCenter;
- }
- z: 100
- }
-
- HifiControls.Keyboard {
- id: keyboard
- raised: parent.keyboardEnabled // Ignore keyboardRaised; keep keyboard raised if enabled (i.e., in HMD).
- numeric: parent.punctuationMode
- anchors {
- bottom: parent.bottom
- left: parent.left
- right: parent.right
- }
- }
- }
-
- function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects.
- // TODO: make available to other .qml.
- var request = new XMLHttpRequest();
- // QT bug: apparently doesn't handle onload. Workaround using readyState.
- request.onreadystatechange = function () {
- var READY_STATE_DONE = 4;
- var HTTP_OK = 200;
- if (request.readyState >= READY_STATE_DONE) {
- var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText,
- response = !error && request.responseText,
- contentType = !error && request.getResponseHeader('content-type');
- if (!error && contentType.indexOf('application/json') === 0) {
- try {
- response = JSON.parse(response);
- } catch (e) {
- error = e;
- }
- }
- cb(error, response);
- }
- };
- request.open("GET", url, true);
- request.send();
- }
-
- function identity(x) {
- return x;
- }
-
- function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey
- if (!error && (data.status === 'success')) {
- return;
- }
- if (!error) { // Create a message from the data
- error = data.status + ': ' + data.error;
- }
- if (typeof(error) === 'string') { // Make a proper Error object
- error = new Error(error);
- }
- error.message += ' in ' + url; // Include the url.
- cb(error);
- return true;
- }
- function resolveUrl(url) {
- return (url.indexOf('/') === 0) ? (addressBarDialog.metaverseServerUrl + url) : url;
- }
-
- function makeModelData(data) { // create a new obj from data
- // ListModel elements will only ever have those properties that are defined by the first obj that is added.
- // So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
- var name = data.place_name,
- tags = data.tags || [data.action, data.username],
- description = data.description || "",
- thumbnail_url = data.thumbnail_url || "";
- return {
- place_name: name,
- username: data.username || "",
- path: data.path || "",
- created_at: data.created_at || "",
- action: data.action || "",
- thumbnail_url: resolveUrl(thumbnail_url),
- image_url: resolveUrl(data.details.image_url),
-
- metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
-
- tags: tags,
- description: description,
- online_users: data.details.concurrency || 0,
- drillDownToPlace: false,
-
- searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
- }
- }
- function suggestable(place) {
- if (place.action === 'snapshot') {
- return true;
- }
- return (place.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
- }
- property var selectedTab: allTab;
- function tabSelect(textButton) {
- selectedTab = textButton;
- fillDestinations();
- }
- property var placeMap: ({});
- function addToSuggestions(place) {
- var collapse = allTab.selected && (place.action !== 'concurrency');
- if (collapse) {
- var existing = placeMap[place.place_name];
- if (existing) {
- existing.drillDownToPlace = true;
- return;
- }
- }
- suggestions.append(place);
- if (collapse) {
- placeMap[place.place_name] = suggestions.get(suggestions.count - 1);
- } else if (place.action === 'concurrency') {
- suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories).
- }
- }
- property int requestId: 0;
- function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model
- var options = [
- 'now=' + new Date().toISOString(),
- 'include_actions=' + selectedTab.includeActions,
- 'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
- 'require_online=true',
- 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
- 'page=' + pageNumber
- ];
- var url = metaverseBase + 'user_stories?' + options.join('&');
- var thisRequestId = ++requestId;
- getRequest(url, function (error, data) {
- if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
- return;
- }
- var stories = data.user_stories.map(function (story) { // explicit single-argument function
- return makeModelData(story, url);
- });
- allStories = allStories.concat(stories);
- stories.forEach(makeFilteredPlaceProcessor());
- if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
- return getUserStoryPage(pageNumber + 1, cb);
- }
- cb();
- });
- }
- function makeFilteredPlaceProcessor() { // answer a function(placeData) that adds it to suggestions if it matches
- var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity),
- data = allStories;
- function matches(place) {
- if (!words.length) {
- return suggestable(place);
- }
- return words.every(function (word) {
- return place.searchText.indexOf(word) >= 0;
- });
- }
- return function (place) {
- if (matches(place)) {
- addToSuggestions(place);
- }
- };
- }
- function filterChoicesByText() {
- suggestions.clear();
- placeMap = {};
- allStories.forEach(makeFilteredPlaceProcessor());
- }
-
- function fillDestinations() {
- allStories = [];
- suggestions.clear();
- placeMap = {};
- getUserStoryPage(1, function (error) {
- console.log('user stories query', error || 'ok', allStories.length);
- });
- }
-
- function updateLocationText(enteringAddress) {
- if (enteringAddress) {
- notice.text = "Go to a place, @user, path or network address";
- notice.color = hifiStyleConstants.colors.baseGrayHighlight;
- } else {
- notice.text = AddressManager.isConnected ? "Your location:" : "Not Connected";
- notice.color = AddressManager.isConnected ? hifiStyleConstants.colors.baseGrayHighlight : hifiStyleConstants.colors.redHighlight;
- // Display hostname, which includes ip address, localhost, and other non-placenames.
- location.text = (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '');
- }
- }
-
- onVisibleChanged: {
- updateLocationText(false);
- if (visible) {
- addressLine.forceActiveFocus();
- fillDestinations();
- }
- }
-
- function toggleOrGo(fromSuggestions) {
- if (addressLine.text !== "") {
- addressBarDialog.loadAddress(addressLine.text, fromSuggestions)
- }
- root.shown = false;
- }
-
- Keys.onPressed: {
- switch (event.key) {
- case Qt.Key_Escape:
- case Qt.Key_Back:
- root.shown = false
- clearAddressLineTimer.start();
- event.accepted = true
- break
- case Qt.Key_Enter:
- case Qt.Key_Return:
- toggleOrGo()
- clearAddressLineTimer.start();
- event.accepted = true
- break
- }
- }
-}
diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml
index 55927fda24..2809f91923 100644
--- a/interface/resources/qml/Browser.qml
+++ b/interface/resources/qml/Browser.qml
@@ -1,7 +1,7 @@
import QtQuick 2.5
import QtQuick.Controls 1.2
import QtWebChannel 1.0
-import QtWebEngine 1.2
+import QtWebEngine 1.5
import "controls-uit"
import "styles" as HifiStyles
diff --git a/interface/resources/qml/controls-uit/BaseWebView.qml b/interface/resources/qml/controls-uit/BaseWebView.qml
index 660f52d529..fdd9c12220 100644
--- a/interface/resources/qml/controls-uit/BaseWebView.qml
+++ b/interface/resources/qml/controls-uit/BaseWebView.qml
@@ -8,7 +8,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-import QtQuick 2.5
+import QtQuick 2.7
import QtWebEngine 1.5
WebEngineView {
diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml
index e54250c872..b0e965e79f 100644
--- a/interface/resources/qml/controls-uit/Key.qml
+++ b/interface/resources/qml/controls-uit/Key.qml
@@ -5,10 +5,16 @@ Item {
id: keyItem
width: 45
height: 50
+
+ property int contentPadding: 4
property string glyph: "a"
property bool toggle: false // does this button have the toggle behaivor?
property bool toggled: false // is this button currently toggled?
property alias mouseArea: mouseArea1
+ property alias fontFamily: letter.font.family;
+ property alias fontPixelSize: letter.font.pixelSize
+ property alias verticalAlignment: letter.verticalAlignment
+ property alias letterAnchors: letter.anchors
function resetToggledMode(mode) {
toggled = mode;
@@ -105,14 +111,8 @@ Item {
color: "#121212"
radius: 2
border.color: "#00000000"
- anchors.right: parent.right
- anchors.rightMargin: 4
- anchors.left: parent.left
- anchors.leftMargin: 4
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 4
- anchors.top: parent.top
- anchors.topMargin: 4
+ anchors.fill: parent
+ anchors.margins: contentPadding
}
Text {
diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml
index 66a61742c9..76b66178d4 100644
--- a/interface/resources/qml/controls-uit/Keyboard.qml
+++ b/interface/resources/qml/controls-uit/Keyboard.qml
@@ -8,7 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-import QtQuick 2.0
+import QtQuick 2.7
+import QtGraphicalEffects 1.0
import "."
Rectangle {
@@ -55,6 +56,8 @@ Rectangle {
return ">";
} else if (str === "/") {
return "?";
+ } else if (str === "-") {
+ return "_";
} else {
return str.toUpperCase(str);
}
@@ -67,6 +70,8 @@ Rectangle {
return ".";
} else if (str === "?") {
return "/";
+ } else if (str === "_") {
+ return "-";
} else {
return str.toLowerCase(str);
}
@@ -85,7 +90,7 @@ Rectangle {
onShiftModeChanged: {
forEachKey(function (key) {
- if (/[a-z]/i.test(key.glyph)) {
+ if (/[a-z-_]/i.test(key.glyph)) {
if (shiftMode) {
key.glyph = keyboardBase.toUpper(key.glyph);
} else {
@@ -112,8 +117,6 @@ Rectangle {
}
Rectangle {
- y: 0
- x: 0
height: showMirrorText ? mirrorTextHeight : 0
width: keyboardWidth
color: "#252525"
@@ -122,13 +125,18 @@ Rectangle {
TextInput {
id: mirrorText
visible: showMirrorText
- FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
- font.family: ralewaySemiBold.name
- font.pointSize: 13.5
+ FontLoader { id: font; source: "../../fonts/FiraSans-Regular.ttf"; }
+ font.family: font.name
+ font.pixelSize: 20
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
- color: "#FFFFFF";
- anchors.fill: parent
+ color: "#00B4EF";
+ anchors.left: parent.left
+ anchors.leftMargin: 10
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+
wrapMode: Text.WordWrap
readOnly: false // we need this to allow control to accept QKeyEvent
selectByMouse: false
@@ -140,16 +148,15 @@ Rectangle {
event.accepted = true;
}
}
- }
- MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
- anchors.fill: parent
+ MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
+ anchors.fill: parent
+ }
}
}
Rectangle {
id: keyboardRect
- x: 0
y: showMirrorText ? mirrorTextHeight : 0
width: keyboardWidth
height: raisedHeight
@@ -158,6 +165,8 @@ Rectangle {
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
+ FontLoader { id: hiFiGlyphs; source: pathToFonts + "fonts/hifi-glyphs.ttf"; }
+
Column {
id: columnAlpha
width: keyboardWidth
@@ -221,7 +230,7 @@ Rectangle {
Key { width: 43; glyph: "b"; }
Key { width: 43; glyph: "n"; }
Key { width: 43; glyph: "m"; }
- Key { width: 43; glyph: "_"; }
+ Key { width: 43; glyph: "-"; }
Key { width: 43; glyph: "/"; }
Key { width: 43; glyph: "?"; }
}
@@ -240,8 +249,13 @@ Rectangle {
Key { width: 231; glyph: " "; }
Key { width: 43; glyph: ","; }
Key { width: 43; glyph: "."; }
- Key { width: 43; glyph: "\u276C"; }
- Key { width: 43; glyph: "\u276D"; }
+ Key {
+ fontFamily: hiFiGlyphs.name;
+ fontPixelSize: 48;
+ letterAnchors.topMargin: -4;
+ verticalAlignment: Text.AlignVCenter;
+ width: 86; glyph: "\ue02b";
+ }
}
}
@@ -328,8 +342,13 @@ Rectangle {
Key { width: 231; glyph: " "; }
Key { width: 43; glyph: ","; }
Key { width: 43; glyph: "."; }
- Key { width: 43; glyph: "\u276C"; }
- Key { width: 43; glyph: "\u276D"; }
+ Key {
+ fontFamily: hiFiGlyphs.name;
+ fontPixelSize: 48;
+ letterAnchors.topMargin: -4;
+ verticalAlignment: Text.AlignVCenter;
+ width: 86; glyph: "\ue02b";
+ }
}
}
}
diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml
index cbc4d19334..1e0a936bf0 100644
--- a/interface/resources/qml/controls/FlickableWebViewCore.qml
+++ b/interface/resources/qml/controls/FlickableWebViewCore.qml
@@ -27,6 +27,12 @@ Item {
id: hifi
}
+ function unfocus() {
+ webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
+ console.log('unfocus completed: ', result);
+ });
+ }
+
function onLoadingChanged(loadRequest) {
if (WebEngineView.LoadStartedStatus === loadRequest.status) {
@@ -129,4 +135,10 @@ Item {
playing: visible
z: 10000
}
+
+ Keys.onPressed: {
+ if ((event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
+ webViewCore.focus = false;
+ }
+ }
}
diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml
index e06ff51569..501e321f0d 100644
--- a/interface/resources/qml/controls/TabletWebScreen.qml
+++ b/interface/resources/qml/controls/TabletWebScreen.qml
@@ -10,6 +10,11 @@ Item {
property alias urlTag: webroot.urlTag
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
+ onKeyboardRaisedChanged: {
+ if(!keyboardRaised) {
+ webroot.unfocus();
+ }
+ }
property bool punctuationMode: false
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml
index 8cd61bc90b..477422cfa1 100644
--- a/interface/resources/qml/controls/TabletWebView.qml
+++ b/interface/resources/qml/controls/TabletWebView.qml
@@ -15,6 +15,11 @@ Item {
property string scriptURL
property bool keyboardEnabled: false
property bool keyboardRaised: false
+ onKeyboardRaisedChanged: {
+ if(!keyboardRaised) {
+ webroot.unfocus();
+ }
+ }
property bool punctuationMode: false
property bool passwordField: false
property bool isDesktop: false
diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml
index 923c8f3fa1..931c64e1ef 100644
--- a/interface/resources/qml/controls/WebView.qml
+++ b/interface/resources/qml/controls/WebView.qml
@@ -12,6 +12,11 @@ Item {
property alias urlTag: webroot.urlTag
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
+ onKeyboardRaisedChanged: {
+ if(!keyboardRaised) {
+ webroot.unfocus();
+ }
+ }
property bool punctuationMode: false
property bool passwordField: false
property alias flickable: webroot.interactive
diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml
index 847111b8a0..e7c68b2a47 100644
--- a/interface/resources/qml/desktop/Desktop.qml
+++ b/interface/resources/qml/desktop/Desktop.qml
@@ -51,19 +51,23 @@ FocusScope {
// The VR version of the primary menu
property var rootMenu: Menu {
+ id: rootMenuId
objectName: "rootMenu"
- // for some reasons it is not possible to use just '({})' here as it gets empty when passed to TableRoot/DesktopRoot
- property var exclusionGroupsByMenuItem : ListModel {}
+ property var exclusionGroups: ({});
+ property Component exclusiveGroupMaker: Component {
+ ExclusiveGroup {
+ }
+ }
- function addExclusionGroup(menuItem, exclusionGroup)
- {
- exclusionGroupsByMenuItem.append(
- {
- 'menuItem' : menuItem.toString(),
- 'exclusionGroup' : exclusionGroup.toString()
- }
- );
+ function addExclusionGroup(qmlAction, exclusionGroup) {
+
+ var exclusionGroupId = exclusionGroup.toString();
+ if(!exclusionGroups[exclusionGroupId]) {
+ exclusionGroups[exclusionGroupId] = exclusiveGroupMaker.createObject(rootMenuId);
+ }
+
+ qmlAction.exclusiveGroup = exclusionGroups[exclusionGroupId]
}
}
diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml
index 5358ad1adc..37c3c2adab 100644
--- a/interface/resources/qml/hifi/AssetServer.qml
+++ b/interface/resources/qml/hifi/AssetServer.qml
@@ -16,10 +16,10 @@ import Qt.labs.settings 1.0
import "../styles-uit"
import "../controls-uit" as HifiControls
-import "../windows"
+import "../windows" as Windows
import "../dialogs"
-ScrollingWindow {
+Windows.ScrollingWindow {
id: root
objectName: "AssetServer"
title: "Asset Browser"
diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml
index ea9ec2f6c9..96bb359aea 100644
--- a/interface/resources/qml/hifi/Desktop.qml
+++ b/interface/resources/qml/hifi/Desktop.qml
@@ -1,6 +1,6 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
-import QtWebEngine 1.1;
+import QtWebEngine 1.5;
import Qt.labs.settings 1.0
import "../desktop" as OriginalDesktop
diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml
index fcfff02b72..1f9caf747a 100644
--- a/interface/resources/qml/hifi/NameCard.qml
+++ b/interface/resources/qml/hifi/NameCard.qml
@@ -432,7 +432,8 @@ Item {
anchors.verticalCenter: nameCardRemoveConnectionImage.verticalCenter
x: 240
onClicked: {
- AddressManager.goToUser(thisNameCard.userName);
+ console.log("Vist user button clicked."); // Remove after debugging.
+ AddressManager.goToUser(thisNameCard.userName, false);
UserActivityLogger.palAction("go_to_user", thisNameCard.userName);
}
}
@@ -441,7 +442,7 @@ Item {
Rectangle {
id: nameCardVUMeter
// Size
- width: isMyCard ? myDisplayName.width - 20 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * (gainSlider.width);
+ width: ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * (gainSlider.width);
height: 8
// Anchors
anchors.bottom: isMyCard ? avatarImage.bottom : parent.bottom;
@@ -525,16 +526,14 @@ Item {
anchors.verticalCenter: nameCardVUMeter.verticalCenter;
anchors.left: nameCardVUMeter.left;
// Properties
- visible: !isMyCard && selected && pal.activeTab == "nearbyTab" && isPresent;
+ visible: (isMyCard || (selected && pal.activeTab == "nearbyTab")) && isPresent;
value: Users.getAvatarGain(uuid)
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
updateValueWhileDragging: true
onValueChanged: {
- if (uuid !== "") {
- updateGainFromQML(uuid, value, false);
- }
+ updateGainFromQML(uuid, value, false);
}
onPressedChanged: {
if (!pressed) {
@@ -574,7 +573,19 @@ Item {
implicitHeight: 16
}
}
- }
+ RalewayRegular {
+ // The slider for my card is special, it controls the master gain
+ id: gainSliderText;
+ visible: isMyCard;
+ text: "master volume";
+ size: hifi.fontSizes.tabularData;
+ anchors.left: parent.right;
+ anchors.leftMargin: 8;
+ color: hifi.colors.baseGrayHighlight;
+ horizontalAlignment: Text.AlignLeft;
+ verticalAlignment: Text.AlignTop;
+ }
+ }
function updateGainFromQML(avatarUuid, sliderValue, isReleased) {
Users.setAvatarGain(avatarUuid, sliderValue);
@@ -594,7 +605,10 @@ Item {
// the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now.
// FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script.
// Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target.
+ // Position avatar 2 metres from the target in the direction that target avatar was facing.
MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2}));
- MyAvatar.orientation = Quat.multiply(avatar.orientation, {y: 1});
+
+ // Rotate avatar on Y axis to face target avatar and cancel out any inherited roll and pitch.
+ MyAvatar.orientation = Quat.cancelOutRollAndPitch(Quat.multiply(avatar.orientation, {y: 1}));
}
}
diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml
index 89f18e5ce2..efcf6ccfcf 100644
--- a/interface/resources/qml/hifi/Pal.qml
+++ b/interface/resources/qml/hifi/Pal.qml
@@ -827,7 +827,7 @@ Rectangle {
hoverEnabled: enabled
enabled: connectionsNameCard.selected && pal.activeTab == "connectionsTab"
onClicked: {
- AddressManager.goToUser(model.userName);
+ AddressManager.goToUser(model.userName, false);
UserActivityLogger.palAction("go_to_user", model.userName);
}
onEntered: connectionsLocationData.color = hifi.colors.blueHighlight;
diff --git a/interface/resources/qml/hifi/WebBrowser.qml b/interface/resources/qml/hifi/WebBrowser.qml
index af54c86bf4..ab93752d92 100644
--- a/interface/resources/qml/hifi/WebBrowser.qml
+++ b/interface/resources/qml/hifi/WebBrowser.qml
@@ -1,3 +1,4 @@
+
//
// WebBrowser.qml
//
@@ -9,12 +10,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-import QtQuick 2.5
-import QtQuick.Controls 1.5 as QQControls
+import QtQuick 2.7
+import QtQuick.Controls 2.2 as QQControls
import QtQuick.Layouts 1.3
-import QtQuick.Controls.Styles 1.4
+import QtGraphicalEffects 1.0
-import QtWebEngine 1.2
+import QtWebEngine 1.5
import QtWebChannel 1.0
import "../styles-uit"
@@ -22,6 +23,8 @@ import "../controls-uit" as HifiControls
import "../windows"
import "../controls"
+import HifiWeb 1.0
+
Rectangle {
id: root;
@@ -32,214 +35,401 @@ Rectangle {
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
property bool punctuationMode: false
+ property var suggestionsList: []
+ readonly property string searchUrlTemplate: "https://www.google.com/search?client=hifibrowser&q=";
+ WebBrowserSuggestionsEngine {
+ id: searchEngine
+
+ onSuggestions: {
+ if (suggestions.length > 0) {
+ suggestionsList = []
+ suggestionsList.push(addressBarInput.text); //do not overwrite edit text
+ for(var i = 0; i < suggestions.length; i++) {
+ suggestionsList.push(suggestions[i]);
+ }
+ addressBar.model = suggestionsList
+ if (!addressBar.popup.visible) {
+ addressBar.popup.open();
+ }
+ }
+ }
+ }
+
+ Timer {
+ id: suggestionRequestTimer
+ interval: 200
+ repeat: false
+ onTriggered: {
+ if (addressBar.editText !== "") {
+ searchEngine.querySuggestions(addressBarInput.text);
+ }
+ }
+ }
+
color: hifi.colors.baseGray;
- // only show the title if loaded through a "loader"
+ function goTo(url) {
+ //must be valid attempt to open an site with dot
+ var urlNew = url
+ if (url.indexOf(".") > 0) {
+ if (url.indexOf("http") < 0) {
+ urlNew = "http://" + url;
+ }
+ } else {
+ urlNew = searchUrlTemplate + url
+ }
+
+ addressBar.model = []
+ //need to rebind if binfing was broken by selecting from suggestions
+ addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
+ webStack.currentItem.webEngineView.url = urlNew
+ suggestionRequestTimer.stop();
+ addressBar.popup.close();
+ }
Column {
spacing: 2
width: parent.width;
RowLayout {
+ id: addressBarRow
width: parent.width;
height: 48
HifiControls.WebGlyphButton {
- enabled: webEngineView.canGoBack
+ enabled: webStack.currentItem.webEngineView.canGoBack || webStack.depth > 1
glyph: hifi.glyphs.backward;
anchors.verticalCenter: parent.verticalCenter;
size: 38;
onClicked: {
- webEngineView.goBack()
+ if (webStack.currentItem.webEngineView.canGoBack) {
+ webStack.currentItem.webEngineView.goBack();
+ } else if (webStack.depth > 1) {
+ webStack.pop();
+ }
}
}
HifiControls.WebGlyphButton {
- enabled: webEngineView.canGoForward
+ enabled: webStack.currentItem.webEngineView.canGoForward
glyph: hifi.glyphs.forward;
anchors.verticalCenter: parent.verticalCenter;
size: 38;
onClicked: {
- webEngineView.goForward()
+ webStack.currentItem.webEngineView.goForward();
}
}
- QQControls.TextField {
+ QQControls.ComboBox {
id: addressBar
- Image {
- anchors.verticalCenter: addressBar.verticalCenter;
- x: 5
- z: 2
- id: faviconImage
- width: 16; height: 16
- sourceSize: Qt.size(width, height)
- source: webEngineView.icon
+ //selectByMouse: true
+ focus: true
+
+ editable: true
+ //flat: true
+ indicator: Item {}
+ background: Item {}
+ onActivated: {
+ goTo(textAt(index));
}
- HifiControls.WebGlyphButton {
- glyph: webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
- anchors.verticalCenter: parent.verticalCenter;
- width: hifi.dimensions.controlLineHeight
- z: 2
- x: addressBar.width - 28
- onClicked: {
- if (webEngineView.loading) {
- webEngineView.stop()
- } else {
- reloadTimer.start()
+ onHighlightedIndexChanged: {
+ if (highlightedIndex >= 0) {
+ addressBar.editText = textAt(highlightedIndex)
+ }
+ }
+
+ popup.height: webStack.height
+
+ onFocusChanged: {
+ if (focus) {
+ addressBarInput.selectAll();
+ }
+ }
+
+ contentItem: QQControls.TextField {
+ id: addressBarInput
+ leftPadding: 26
+ rightPadding: hifi.dimensions.controlLineHeight + 5
+ text: addressBar.editText
+ placeholderText: qsTr("Enter URL")
+ font: addressBar.font
+ selectByMouse: true
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ onFocusChanged: {
+ if (focus) {
+ selectAll();
+ }
+ }
+
+ Keys.onDeletePressed: {
+ addressBarInput.text = ""
+ }
+
+ Keys.onPressed: {
+ if (event.key === Qt.Key_Return) {
+ goTo(addressBarInput.text);
+ event.accepted = true;
+ }
+ }
+
+ Image {
+ anchors.verticalCenter: parent.verticalCenter;
+ x: 5
+ z: 2
+ id: faviconImage
+ width: 16; height: 16
+ sourceSize: Qt.size(width, height)
+ source: webStack.currentItem.webEngineView.icon
+ }
+
+ HifiControls.WebGlyphButton {
+ glyph: webStack.currentItem.webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
+ anchors.verticalCenter: parent.verticalCenter;
+ width: hifi.dimensions.controlLineHeight
+ z: 2
+ x: addressBarInput.width - implicitWidth
+ onClicked: {
+ if (webStack.currentItem.webEngineView.loading) {
+ webStack.currentItem.webEngineView.stop();
+ } else {
+ webStack.currentItem.reloadTimer.start();
+ }
}
}
}
- style: TextFieldStyle {
- padding {
- left: 26;
- right: 26
+ Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i");
+
+ Keys.onPressed: {
+ if (event.key === Qt.Key_Return) {
+ goTo(addressBarInput.text);
+ event.accepted = true;
}
}
- focus: true
+
+ onEditTextChanged: {
+ if (addressBar.editText !== "" && addressBar.editText !== webStack.currentItem.webEngineView.url.toString()) {
+ suggestionRequestTimer.restart();
+ } else {
+ addressBar.model = []
+ addressBar.popup.close();
+ }
+
+ }
+
Layout.fillWidth: true
- text: webEngineView.url
- onAccepted: webEngineView.url = text
+ editText: webStack.currentItem.webEngineView.url
+ onAccepted: goTo(addressBarInput.text);
}
+
HifiControls.WebGlyphButton {
checkable: true
- //only QtWebEngine 1.3
- //checked: webEngineView.audioMuted
+ checked: webStack.currentItem.webEngineView.audioMuted
glyph: checked ? hifi.glyphs.unmuted : hifi.glyphs.muted
anchors.verticalCenter: parent.verticalCenter;
width: hifi.dimensions.controlLineHeight
onClicked: {
- webEngineView.triggerWebAction(WebEngineView.ToggleMediaMute)
+ webStack.currentItem.webEngineView.audioMuted = !webStack.currentItem.webEngineView.audioMuted
}
}
}
QQControls.ProgressBar {
id: loadProgressBar
- style: ProgressBarStyle {
- background: Rectangle {
- color: "#6A6A6A"
- }
- progress: Rectangle{
+ background: Rectangle {
+ implicitHeight: 2
+ color: "#6A6A6A"
+ }
+
+ contentItem: Item {
+ implicitHeight: 2
+
+ Rectangle {
+ width: loadProgressBar.visualPosition * parent.width
+ height: parent.height
color: "#00B4EF"
}
}
width: parent.width;
- minimumValue: 0
- maximumValue: 100
- value: webEngineView.loadProgress
+ from: 0
+ to: 100
+ value: webStack.currentItem.webEngineView.loadProgress
height: 2
}
- HifiControls.BaseWebView {
- id: webEngineView
- focus: true
- objectName: "tabletWebEngineView"
+ Component {
+ id: webViewComponent
+ Rectangle {
+ property alias webEngineView: webEngineView
+ property alias reloadTimer: reloadTimer
- url: "http://www.highfidelity.com"
- property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
+ property WebEngineNewViewRequest request: null
- width: parent.width;
- height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
+ property bool isDialog: QQControls.StackView.index > 0
+ property real margins: isDialog ? 10 : 0
- profile: HFWebEngineProfile;
+ color: "#d1d1d1"
- property string userScriptUrl: ""
-
- // creates a global EventBridge object.
- WebEngineScript {
- id: createGlobalEventBridge
- sourceCode: eventBridgeJavaScriptToInject
- injectionPoint: WebEngineScript.DocumentCreation
- worldId: WebEngineScript.MainWorld
- }
-
- // detects when to raise and lower virtual keyboard
- WebEngineScript {
- id: raiseAndLowerKeyboard
- injectionPoint: WebEngineScript.Deferred
- sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
- worldId: WebEngineScript.MainWorld
- }
-
- // User script.
- WebEngineScript {
- id: userScript
- sourceUrl: webEngineView.userScriptUrl
- injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
- worldId: WebEngineScript.MainWorld
- }
-
- userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
-
- settings.autoLoadImages: true
- settings.javascriptEnabled: true
- settings.errorPageEnabled: true
- settings.pluginsEnabled: true
- settings.fullScreenSupportEnabled: false
- //from WebEngine 1.3
- // settings.autoLoadIconsForPage: false
- // settings.touchIconsEnabled: false
-
- onCertificateError: {
- error.defer();
- }
-
- Component.onCompleted: {
- webChannel.registerObject("eventBridge", eventBridge);
- webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
- webEngineView.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
- }
-
- onFeaturePermissionRequested: {
- grantFeaturePermission(securityOrigin, feature, true);
- }
-
- onNewViewRequested: {
- if (!request.userInitiated) {
- print("Warning: Blocked a popup window.");
- }
- }
-
- onRenderProcessTerminated: {
- var status = "";
- switch (terminationStatus) {
- case WebEngineView.NormalTerminationStatus:
- status = "(normal exit)";
- break;
- case WebEngineView.AbnormalTerminationStatus:
- status = "(abnormal exit)";
- break;
- case WebEngineView.CrashedTerminationStatus:
- status = "(crashed)";
- break;
- case WebEngineView.KilledTerminationStatus:
- status = "(killed)";
- break;
+ QQControls.StackView.onActivated: {
+ addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
}
- print("Render process exited with code " + exitCode + " " + status);
- reloadTimer.running = true;
- }
+ onRequestChanged: {
+ if (isDialog && request !== null && request !== undefined) {//is Dialog ?
+ request.openIn(webEngineView);
+ }
+ }
- onWindowCloseRequested: {
- }
+ HifiControls.BaseWebView {
+ id: webEngineView
+ anchors.fill: parent
+ anchors.margins: parent.margins
- Timer {
- id: reloadTimer
- interval: 0
- running: false
- repeat: false
- onTriggered: webEngineView.reload()
+ layer.enabled: parent.isDialog
+ layer.effect: DropShadow {
+ verticalOffset: 8
+ horizontalOffset: 8
+ color: "#330066ff"
+ samples: 10
+ spread: 0.5
+ }
+
+ focus: true
+ objectName: "tabletWebEngineView"
+
+ //profile: HFWebEngineProfile;
+ profile.httpUserAgent: "Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0"
+
+ property string userScriptUrl: ""
+
+ onLoadingChanged: {
+ if (!loading) {
+ addressBarInput.cursorPosition = 0 //set input field cursot to beginning
+ suggestionRequestTimer.stop();
+ addressBar.popup.close();
+ }
+ }
+
+ onLinkHovered: {
+ //TODO: change cursor shape?
+ }
+
+ // creates a global EventBridge object.
+ WebEngineScript {
+ id: createGlobalEventBridge
+ sourceCode: eventBridgeJavaScriptToInject
+ injectionPoint: WebEngineScript.Deferred
+ worldId: WebEngineScript.MainWorld
+ }
+
+ // detects when to raise and lower virtual keyboard
+ WebEngineScript {
+ id: raiseAndLowerKeyboard
+ injectionPoint: WebEngineScript.Deferred
+ sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
+ worldId: WebEngineScript.MainWorld
+ }
+
+ // User script.
+ WebEngineScript {
+ id: userScript
+ sourceUrl: webEngineView.userScriptUrl
+ injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
+ worldId: WebEngineScript.MainWorld
+ }
+
+ userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
+
+ settings.autoLoadImages: true
+ settings.javascriptEnabled: true
+ settings.errorPageEnabled: true
+ settings.pluginsEnabled: true
+ settings.fullScreenSupportEnabled: true
+ settings.autoLoadIconsForPage: true
+ settings.touchIconsEnabled: true
+
+ onCertificateError: {
+ error.defer();
+ }
+
+ Component.onCompleted: {
+ webChannel.registerObject("eventBridge", eventBridge);
+ webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+ }
+
+ onFeaturePermissionRequested: {
+ grantFeaturePermission(securityOrigin, feature, true);
+ }
+
+ onNewViewRequested: {
+ if (request.destination == WebEngineView.NewViewInDialog) {
+ webStack.push(webViewComponent, {"request": request});
+ } else {
+ request.openIn(webEngineView);
+ }
+ }
+
+ onRenderProcessTerminated: {
+ var status = "";
+ switch (terminationStatus) {
+ case WebEngineView.NormalTerminationStatus:
+ status = "(normal exit)";
+ break;
+ case WebEngineView.AbnormalTerminationStatus:
+ status = "(abnormal exit)";
+ break;
+ case WebEngineView.CrashedTerminationStatus:
+ status = "(crashed)";
+ break;
+ case WebEngineView.KilledTerminationStatus:
+ status = "(killed)";
+ break;
+ }
+
+ console.error("Render process exited with code " + exitCode + " " + status);
+ reloadTimer.running = true;
+ }
+
+ onFullScreenRequested: {
+ if (request.toggleOn) {
+ webEngineView.state = "FullScreen";
+ } else {
+ webEngineView.state = "";
+ }
+ request.accept();
+ }
+
+ onWindowCloseRequested: {
+ webStack.pop();
+ }
+ }
+ Timer {
+ id: reloadTimer
+ interval: 0
+ running: false
+ repeat: false
+ onTriggered: webEngineView.reload()
+ }
}
}
+
+ QQControls.StackView {
+ id: webStack
+ width: parent.width;
+ property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
+ height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
+
+ Component.onCompleted: webStack.push(webViewComponent, {"webEngineView.url": "https://www.highfidelity.com"});
+ }
}
+
HifiControls.Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml
index 12c2ec1835..87ddce49ca 100644
--- a/interface/resources/qml/hifi/audio/Audio.qml
+++ b/interface/resources/qml/hifi/audio/Audio.qml
@@ -213,8 +213,8 @@ Rectangle {
anchors.right: parent.right
peak: model.peak;
anchors.verticalCenter: parent.verticalCenter
- visible: (bar.currentIndex === 1 && selectedHMD && isVR) ||
- (bar.currentIndex === 0 && selectedDesktop && !isVR) &&
+ visible: ((bar.currentIndex === 1 && isVR) ||
+ (bar.currentIndex === 0 && !isVR)) &&
Audio.devices.input.peakValuesAvailable;
}
}
diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index dfe0c319e5..6c4e020694 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -596,9 +596,7 @@ Rectangle {
anchors.right: parent.right;
text: root.isWearable ? "Wear It" : "Rez It"
onClicked: {
- if (urlHandler.canHandleUrl(root.itemHref)) {
- urlHandler.handleUrl(root.itemHref);
- }
+ sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
}
diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
index aa1372494f..14ed9ece67 100644
--- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
+++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
@@ -33,6 +33,7 @@ Rectangle {
property string dateOfPurchase: "--";
property bool isLightbox: false;
property bool isMyCert: false;
+ property bool isCertificateInvalid: false;
// Style
color: hifi.colors.faintGray;
Hifi.QmlCommerce {
@@ -44,10 +45,11 @@ Rectangle {
} else {
root.marketplaceUrl = result.data.marketplace_item_url;
root.isMyCert = result.isMyCert ? result.isMyCert : false;
- root.itemOwner = root.isMyCert ? Account.username :
- "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
- root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run);
- root.dateOfPurchase = getFormattedDate(result.data.transfer_created_at * 1000);
+ root.itemOwner = root.isCertificateInvalid ? "--" : (root.isMyCert ? Account.username :
+ "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022");
+ root.itemEdition = root.isCertificateInvalid ? "Uncertified Copy" :
+ (result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run));
+ root.dateOfPurchase = root.isCertificateInvalid ? "" : getFormattedDate(result.data.transfer_created_at * 1000);
root.itemName = result.data.marketplace_item_name;
if (result.data.invalid_reason || result.data.transfer_status[0] === "failed") {
@@ -65,6 +67,44 @@ Rectangle {
}
}
}
+
+ onUpdateCertificateStatus: {
+ if (certStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS
+ // NOP
+ } else if (certStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT
+ root.isCertificateInvalid = true;
+ errorText.text = "Verification of this certificate timed out.";
+ errorText.color = hifi.colors.redHighlight;
+ } else if (certStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED
+ root.isCertificateInvalid = true;
+ titleBarText.text = "Invalid Certificate";
+ titleBarText.color = hifi.colors.redHighlight;
+
+ popText.text = "";
+ root.itemOwner = "";
+ dateOfPurchaseHeader.text = "";
+ root.dateOfPurchase = "";
+ root.itemEdition = "Uncertified Copy";
+
+ errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item.";
+ errorText.color = hifi.colors.baseGray;
+ } else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
+ root.isCertificateInvalid = true;
+ titleBarText.text = "Invalid Certificate";
+ titleBarText.color = hifi.colors.redHighlight;
+
+ popText.text = "";
+ root.itemOwner = "";
+ dateOfPurchaseHeader.text = "";
+ root.dateOfPurchase = "";
+ root.itemEdition = "Uncertified Copy";
+
+ errorText.text = "The avatar who rezzed this item doesn't own it.";
+ errorText.color = hifi.colors.baseGray;
+ } else {
+ console.log("Unknown certificate status received from ledger signal!");
+ }
+ }
}
onCertificateIdChanged: {
@@ -73,21 +113,6 @@ Rectangle {
}
}
- onVisibleChanged: {
- if (!visible) {
- titleBarText.text = "Certificate";
- popText.text = "PROOF OF PURCHASE";
- root.certificateId = "";
- root.itemName = "--";
- root.itemOwner = "--";
- root.itemEdition = "--";
- root.dateOfPurchase = "--";
- root.marketplaceUrl = "";
- root.isMyCert = false;
- errorText.text = "";
- }
- }
-
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
@@ -216,7 +241,7 @@ Rectangle {
}
AnonymousProRegular {
id: isMyCertText;
- visible: root.isMyCert;
+ visible: root.isMyCert && !root.isCertificateInvalid;
text: "(Private)";
size: 18;
// Anchors
@@ -380,6 +405,18 @@ Rectangle {
case 'inspectionCertificate_setCertificateId':
root.certificateId = message.certificateId;
break;
+ case 'inspectionCertificate_resetCert':
+ titleBarText.text = "Certificate";
+ popText.text = "PROOF OF PURCHASE";
+ root.certificateId = "";
+ root.itemName = "--";
+ root.itemOwner = "--";
+ root.itemEdition = "--";
+ root.dateOfPurchase = "--";
+ root.marketplaceUrl = "";
+ root.isMyCert = false;
+ errorText.text = "";
+ break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
index fb42865ba4..15ebada0c4 100644
--- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
@@ -346,9 +346,7 @@ Item {
enabled: (root.canRezCertifiedItems || root.isWearable) && root.purchaseStatus !== "invalidated";
onClicked: {
- if (urlHandler.canHandleUrl(root.itemHref)) {
- urlHandler.handleUrl(root.itemHref);
- }
+ sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
}
diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
index f292f9603e..4c10bdc097 100644
--- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
@@ -442,6 +442,8 @@ Rectangle {
onSendToPurchases: {
if (msg.method === 'purchases_itemInfoClicked') {
sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId});
+ } else if (msg.method === "purchases_rezClicked") {
+ sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, isWearable: isWearable});
} else if (msg.method === 'purchases_itemCertificateClicked') {
inspectionCertificate.visible = true;
inspectionCertificate.isLightbox = true;
@@ -638,7 +640,8 @@ Rectangle {
if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) {
if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) {
filteredPurchasesModel.insert(0, purchasesModel.get(i));
- } else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === -1) || !root.isShowingMyItems) {
+ } else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === "0") ||
+ (!root.isShowingMyItems && purchasesModel.get(i).edition_number !== "0")) {
filteredPurchasesModel.append(purchasesModel.get(i));
}
}
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
index 1fe0dcc58b..fd7ce0fdfd 100644
--- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
@@ -196,7 +196,38 @@ Item {
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
- anchors.rightMargin: 24;
+
+ Item {
+ visible: transactionHistoryModel.count === 0 && root.historyReceived;
+ anchors.centerIn: parent;
+ width: parent.width - 12;
+ height: parent.height;
+
+ HifiControlsUit.Separator {
+ colorScheme: 1;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ anchors.top: parent.top;
+ }
+
+ RalewayRegular {
+ id: noActivityText;
+ text: "The Wallet app is in closed Beta.
To request entry and receive free HFC, please contact " +
+ "info@highfidelity.com with your High Fidelity account username and the email address registered to that account.";
+ // Text size
+ size: 24;
+ // Style
+ color: hifi.colors.blueAccent;
+ anchors.left: parent.left;
+ anchors.leftMargin: 12;
+ anchors.right: parent.right;
+ anchors.rightMargin: 12;
+ anchors.verticalCenter: parent.verticalCenter;
+ height: paintedHeight;
+ wrapMode: Text.WordWrap;
+ horizontalAlignment: Text.AlignHCenter;
+ }
+ }
ListView {
id: transactionHistory;
@@ -294,17 +325,6 @@ Item {
}
}
}
-
- // This should never be visible (since you immediately get 100 HFC)
- FiraSansRegular {
- id: emptyTransationHistory;
- size: 24;
- visible: !transactionHistory.visible && root.historyReceived;
- text: "Recent Activity Unavailable";
- anchors.fill: parent;
- horizontalAlignment: Text.AlignHCenter;
- verticalAlignment: Text.AlignVCenter;
- }
}
}
@@ -350,7 +370,9 @@ Item {
}
root.pendingCount = pendingCount;
- transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
+ if (pendingCount > 0) {
+ transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
+ }
}
//
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml
index 99fe933bd6..1a62fe6f0d 100644
--- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml
@@ -53,8 +53,6 @@ Item {
onWalletAuthenticatedStatusResult: {
if (isAuthenticated) {
root.activeView = "step_4";
- } else {
- root.activeView = "step_3";
}
}
diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml
index 80c1b58444..83f91c78c5 100644
--- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml
+++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml
@@ -32,6 +32,8 @@ Rectangle {
color: hifi.colors.baseGray
+ property bool keyboardEnabled: HMD.active
+ property bool keyboardRaised: false
LetterboxMessage {
id: letterBoxMessage
@@ -380,7 +382,7 @@ Rectangle {
Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i")
onActiveFocusChanged: {
// raise the keyboard
- keyboard.raised = activeFocus;
+ root.keyboardRaised = activeFocus;
// scroll to the bottom of the content area.
if (activeFocus) {
@@ -481,7 +483,7 @@ Rectangle {
HifiControls.Keyboard {
id: keyboard
- raised: false
+ raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: false
anchors {
bottom: parent.bottom
diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml
index 47d28486a9..3debc8b9e7 100644
--- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml
+++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml
@@ -11,8 +11,11 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
+import QtQuick.Dialogs 1.2 as OriginalDialogs
+
import "../../styles-uit"
import "../../controls-uit"
+import "../dialogs"
Rectangle {
id: newModelDialog
@@ -25,6 +28,15 @@ Rectangle {
property bool punctuationMode: false
property bool keyboardRasied: false
+ function errorMessageBox(message) {
+ return desktop.messageBox({
+ icon: hifi.icons.warning,
+ defaultButton: OriginalDialogs.StandardButton.Ok,
+ title: "Error",
+ text: message
+ });
+ }
+
Item {
id: column1
anchors.rightMargin: 10
@@ -98,7 +110,6 @@ Rectangle {
CheckBox {
id: dynamic
text: qsTr("Dynamic")
-
}
Row {
@@ -117,6 +128,7 @@ Rectangle {
Text {
id: text2
width: 160
+ x: dynamic.width / 2
color: "#ffffff"
text: qsTr("Models with automatic collisions set to 'Exact' cannot be dynamic, and should not be used as floors")
wrapMode: Text.WordWrap
@@ -139,15 +151,40 @@ Rectangle {
ComboBox {
id: collisionType
+
+ property int priorIndex: 0
+ property string staticMeshCollisionText: "Exact - All polygons"
+ property var collisionArray: ["No Collision",
+ "Basic - Whole model",
+ "Good - Sub-meshes",
+ staticMeshCollisionText,
+ "Box",
+ "Sphere"]
+
width: 200
z: 100
transformOrigin: Item.Center
- model: ["No Collision",
- "Basic - Whole model",
- "Good - Sub-meshes",
- "Exact - All polygons",
- "Box",
- "Sphere"]
+ model: collisionArray
+
+ onCurrentIndexChanged: {
+ if (collisionArray[currentIndex] === staticMeshCollisionText) {
+
+ if (dynamic.checked) {
+ currentIndex = priorIndex;
+
+ errorMessageBox("Models with Automatic Collisions set to \""
+ + staticMeshCollisionText + "\" cannot be dynamic.");
+ //--EARLY EXIT--( Can't have a static mesh model that's dynamic )
+ return;
+ }
+
+ dynamic.enabled = false;
+ } else {
+ dynamic.enabled = true;
+ }
+
+ priorIndex = currentIndex;
+ }
}
Row {
@@ -155,10 +192,10 @@ Rectangle {
width: 200
height: 400
spacing: 5
-
- anchors {
- rightMargin: 15
- }
+
+ anchors.horizontalCenter: column3.horizontalCenter
+ anchors.horizontalCenterOffset: -20
+
Button {
id: button1
text: qsTr("Add")
diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml
index 66e3dfdbbb..2a086bae94 100644
--- a/interface/resources/qml/hifi/tablet/Tablet.qml
+++ b/interface/resources/qml/hifi/tablet/Tablet.qml
@@ -8,8 +8,8 @@ import "../audio" as HifiAudio
Item {
id: tablet
objectName: "tablet"
- property int rowIndex: 0
- property int columnIndex: 0
+ property int rowIndex: 6 // by default
+ property int columnIndex: 1 // point to 'go to location'
property int count: (flowMain.children.length - 1)
// used to look up a button by its uuid
diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml
index 4d9a83817a..6aa3b8e7fe 100644
--- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml
+++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml
@@ -11,6 +11,7 @@
import Hifi 1.0
import QtQuick 2.5
import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import "../../controls"
import "../../styles"
@@ -83,7 +84,6 @@ StackView {
anchors.centerIn = parent;
}
-
function resetAfterTeleport() {
//storyCardFrame.shown = root.shown = false;
}
@@ -134,7 +134,8 @@ StackView {
bottom: parent.bottom
}
- onHostChanged: updateLocationTextTimer.start();
+ onHostChanged: updateLocationTextTimer.restart();
+
Rectangle {
id: navBar
width: parent.width
@@ -205,16 +206,16 @@ StackView {
anchors {
top: parent.top;
left: addressLineContainer.left;
- right: addressLineContainer.right;
}
}
HifiStyles.FiraSansRegular {
id: location;
anchors {
- left: addressLineContainer.left;
- leftMargin: 8;
- verticalCenter: addressLineContainer.verticalCenter;
+ left: notice.right
+ leftMargin: 8
+ right: addressLineContainer.right
+ verticalCenter: notice.verticalCenter
}
font.pixelSize: addressLine.font.pixelSize;
color: "gray";
@@ -222,7 +223,7 @@ StackView {
visible: addressLine.text.length === 0
}
- TextInput {
+ TextField {
id: addressLine
width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin;
anchors {
@@ -230,7 +231,6 @@ StackView {
leftMargin: 8;
verticalCenter: addressLineContainer.verticalCenter;
}
- font.pixelSize: hifi.fonts.pixelSize * 0.75
onTextChanged: {
updateLocationText(text.length > 0);
}
@@ -238,6 +238,17 @@ StackView {
addressBarDialog.keyboardEnabled = false;
toggleOrGo();
}
+ placeholderText: "Type domain address here"
+ verticalAlignment: TextInput.AlignBottom
+ style: TextFieldStyle {
+ textColor: hifi.colors.text
+ placeholderTextColor: "gray"
+ font {
+ family: hifi.fonts.fontFamily
+ pixelSize: hifi.fonts.pixelSize * 0.75
+ }
+ background: Item {}
+ }
}
Rectangle {
@@ -347,7 +358,7 @@ StackView {
// Delay updating location text a bit to avoid flicker of content and so that connection status is valid.
id: updateLocationTextTimer
running: false
- interval: 500 // ms
+ interval: 1000 // ms
repeat: false
onTriggered: updateLocationText(false);
}
@@ -366,7 +377,7 @@ StackView {
HifiControls.Keyboard {
id: keyboard
- raised: parent.keyboardEnabled
+ raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
bottom: parent.bottom
diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml
index 169c7acec1..8fc31d1cd6 100644
--- a/interface/resources/qml/hifi/tablet/TabletButton.qml
+++ b/interface/resources/qml/hifi/tablet/TabletButton.qml
@@ -5,7 +5,9 @@ import TabletScriptingInterface 1.0
Item {
id: tabletButton
- property string captionColorOverride: ""
+ property color defaultCaptionColor: "#ffffff"
+ property color captionColor: defaultCaptionColor
+
property var uuid;
property string icon: "icons/tablet-icons/edit-i.svg"
property string hoverIcon: tabletButton.icon
@@ -105,7 +107,7 @@ Item {
Text {
id: text
- color: captionColorOverride !== "" ? captionColorOverride: "#ffffff"
+ color: captionColor
text: tabletButton.text
font.bold: true
font.pixelSize: 18
@@ -171,7 +173,7 @@ Item {
PropertyChanges {
target: text
- color: captionColorOverride !== "" ? captionColorOverride: "#ffffff"
+ color: captionColor
text: tabletButton.hoverText
}
@@ -197,7 +199,7 @@ Item {
PropertyChanges {
target: text
- color: captionColorOverride !== "" ? captionColorOverride: "#333333"
+ color: captionColor !== defaultCaptionColor ? captionColor : "#333333"
text: tabletButton.activeText
}
@@ -228,7 +230,7 @@ Item {
PropertyChanges {
target: text
- color: captionColorOverride !== "" ? captionColorOverride: "#333333"
+ color: captionColor !== defaultCaptionColor ? captionColor : "#333333"
text: tabletButton.activeHoverText
}
diff --git a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml
index 11d3cab35e..520841b33f 100644
--- a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml
+++ b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml
@@ -40,37 +40,29 @@ Item {
CheckBox {
id: checkbox
- // FIXME: Should use radio buttons if source.exclusiveGroup.
+
width: 20
visible: source !== null ?
source.visible && source.type === 1 && source.checkable && !source.exclusiveGroup :
false
- checked: setChecked()
- function setChecked() {
- if (!source || source.type !== 1 || !source.checkable) {
- return false;
- }
- // FIXME this works for native QML menus but I don't think it will
- // for proxied QML menus
- return source.checked;
+
+ Binding on checked {
+ value: source.checked;
+ when: source && source.type === 1 && source.checkable && !source.exclusiveGroup;
}
}
RadioButton {
id: radiobutton
- // FIXME: Should use radio buttons if source.exclusiveGroup.
+
width: 20
visible: source !== null ?
source.visible && source.type === 1 && source.checkable && source.exclusiveGroup :
false
- checked: setChecked()
- function setChecked() {
- if (!source || source.type !== 1 || !source.checkable) {
- return false;
- }
- // FIXME this works for native QML menus but I don't think it will
- // for proxied QML menus
- return source.checked;
+
+ Binding on checked {
+ value: source.checked;
+ when: source && source.type === 1 && source.checkable && source.exclusiveGroup;
}
}
}
diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml
index ce4fac3bd5..8cd696a41b 100644
--- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml
+++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml
@@ -66,7 +66,6 @@ Item {
function toModel(items, newMenu) {
var result = modelMaker.createObject(tabletMenu);
- var exclusionGroups = {};
for (var i = 0; i < items.length; ++i) {
var item = items[i];
@@ -78,28 +77,6 @@ Item {
if (item.text !== "Users Online") {
result.append({"name": item.text, "item": item})
}
-
- for(var j = 0; j < tabletMenu.rootMenu.exclusionGroupsByMenuItem.count; ++j)
- {
- var entry = tabletMenu.rootMenu.exclusionGroupsByMenuItem.get(j);
- if(entry.menuItem == item.toString())
- {
- var exclusionGroupId = entry.exclusionGroup;
- console.debug('item exclusionGroupId: ', exclusionGroupId)
-
- if(!exclusionGroups[exclusionGroupId])
- {
- exclusionGroups[exclusionGroupId] = exclusiveGroupMaker.createObject(newMenu);
- console.debug('new exclusion group created: ', exclusionGroups[exclusionGroupId])
- }
-
- var exclusionGroup = exclusionGroups[exclusionGroupId];
-
- item.exclusiveGroup = exclusionGroup
- console.debug('item.exclusiveGroup: ', item.exclusiveGroup)
- }
- }
-
break;
case MenuItemType.Separator:
result.append({"name": "", "item": item})
diff --git a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml
index 63149ad23b..f27ba6bb24 100644
--- a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml
+++ b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml
@@ -4,7 +4,9 @@ import QtQuick.Controls 1.4
StateImage {
id: button
- property string captionColorOverride: ""
+ property color defaultCaptionColor: "#ffffff"
+ property color captionColor: defaultCaptionColor
+
property bool buttonEnabled: true
property bool isActive: false
property bool isEntered: false
@@ -98,7 +100,7 @@ StateImage {
Text {
id: caption
- color: captionColorOverride !== "" ? captionColorOverride: (button.isActive ? "#000000" : "#ffffff")
+ color: button.isActive ? "#000000" : captionColor
text: button.isActive ? (button.isEntered ? button.activeHoverText : button.activeText) : (button.isEntered ? button.hoverText : button.text)
font.bold: false
font.pixelSize: 9
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 387a5b60c1..ca5303bcb8 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -203,6 +203,8 @@
#include "commerce/Wallet.h"
#include "commerce/QmlCommerce.h"
+#include "webbrowser/WebBrowserSuggestionsEngine.h"
+
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
#if defined(Q_OS_WIN)
@@ -1392,7 +1394,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
-
QTimer* settingsTimer = new QTimer();
moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{
connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{
@@ -1700,8 +1701,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
lastLeftHandPose = leftHandPose;
lastRightHandPose = rightHandPose;
- properties["local_socket_changes"] = DependencyManager::get()->getStat(LOCAL_SOCKET_CHANGE_STAT).toInt();
-
UserActivityLogger::getInstance().logAction("stats", properties);
});
sendStatsTimer->start();
@@ -1825,6 +1824,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Preload Tablet sounds
DependencyManager::get()->preloadSounds();
+ _pendingIdleEvent = false;
+ _pendingRenderEvent = false;
+
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
}
@@ -2223,6 +2225,7 @@ void Application::initializeUi() {
QmlCommerce::registerType();
qmlRegisterType("Hifi", 1, 0, "ResourceImageItem");
qmlRegisterType("Hifi", 1, 0, "Preference");
+ qmlRegisterType("HifiWeb", 1, 0, "WebBrowserSuggestionsEngine");
auto offscreenUi = DependencyManager::get();
offscreenUi->create();
@@ -2693,7 +2696,7 @@ bool Application::importFromZIP(const QString& filePath) {
qDebug() << "A zip file has been dropped in: " << filePath;
QUrl empty;
// handle Blocks download from Marketplace
- if (filePath.contains("vr.google.com/downloads")) {
+ if (filePath.contains("poly.google.com/downloads")) {
addAssetToWorldFromURL(filePath);
} else {
qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false);
@@ -4441,7 +4444,7 @@ void Application::cameraModeChanged() {
void Application::cameraMenuChanged() {
auto menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
- if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
+ if (!isHMDMode() && _myCamera.getMode() != CAMERA_MODE_MIRROR) {
_myCamera.setMode(CAMERA_MODE_MIRROR);
getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers
}
@@ -4481,8 +4484,11 @@ void Application::resetPhysicsReadyInformation() {
void Application::reloadResourceCaches() {
resetPhysicsReadyInformation();
+
// Query the octree to refresh everything in view
_lastQueriedTime = 0;
+ _octreeQuery.incrementConnectionID();
+
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
DependencyManager::get()->clearCache();
@@ -5543,6 +5549,7 @@ void Application::nodeActivated(SharedNodePointer node) {
// so we will do a proper query during update
if (node->getType() == NodeType::EntityServer) {
_lastQueriedTime = 0;
+ _octreeQuery.incrementConnectionID();
}
if (node->getType() == NodeType::AudioMixer) {
@@ -6235,7 +6242,7 @@ void Application::addAssetToWorldFromURL(QString url) {
if (url.contains("filename")) {
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
}
- if (url.contains("vr.google.com/downloads")) {
+ if (url.contains("poly.google.com/downloads")) {
filename = url.section('/', -1);
if (url.contains("noDownload")) {
filename.remove(".zip?noDownload=false");
@@ -6270,7 +6277,7 @@ void Application::addAssetToWorldFromURLRequestFinished() {
if (url.contains("filename")) {
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
}
- if (url.contains("vr.google.com/downloads")) {
+ if (url.contains("poly.google.com/downloads")) {
filename = url.section('/', -1);
if (url.contains("noDownload")) {
filename.remove(".zip?noDownload=false");
@@ -7082,6 +7089,7 @@ DisplayPluginPointer Application::getActiveDisplayPlugin() const {
return _displayPlugin;
}
+static const char* EXCLUSION_GROUP_KEY = "exclusionGroup";
static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
auto menu = Menu::getInstance();
@@ -7117,6 +7125,8 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti
action->setCheckable(true);
action->setChecked(active);
displayPluginGroup->addAction(action);
+
+ action->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(displayPluginGroup));
Q_ASSERT(menu->menuItemExists(MenuOption::OutputMenu, name));
}
@@ -7271,6 +7281,10 @@ void Application::updateDisplayMode() {
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
cameraMenuChanged();
}
+
+ // Remove the mirror camera option from menu if in HMD mode
+ auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror);
+ mirrorAction->setVisible(!isHmd);
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index fbfb3979be..c709571204 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -543,7 +543,7 @@ private:
ViewFrustum _displayViewFrustum;
quint64 _lastQueriedTime;
- OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
+ OctreeQuery _octreeQuery { true }; // NodeData derived class for querying octee cells from octree servers
std::shared_ptr _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session
std::shared_ptr _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
@@ -708,7 +708,7 @@ private:
friend class RenderEventHandler;
- std::atomic _pendingIdleEvent { false };
- std::atomic _pendingRenderEvent { false };
+ std::atomic _pendingIdleEvent { true };
+ std::atomic _pendingRenderEvent { true };
};
#endif // hifi_Application_h
diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp
index d3c8746e16..01ccbd0d9a 100644
--- a/interface/src/LODManager.cpp
+++ b/interface/src/LODManager.cpp
@@ -54,7 +54,9 @@ void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float delta
float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET;
float maxTime = glm::max(renderTime, engineRunTime);
const float BLEND_TIMESCALE = 0.3f; // sec
- float blend = BLEND_TIMESCALE / deltaTimeSec;
+ const float MIN_DELTA_TIME = 0.001f;
+ const float safeDeltaTime = glm::max(deltaTimeSec, MIN_DELTA_TIME);
+ float blend = BLEND_TIMESCALE / safeDeltaTime;
if (blend > 1.0f) {
blend = 1.0f;
}
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 9df22ab08e..9ec5cc6034 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -56,7 +56,7 @@ Menu* Menu::getInstance() {
return dynamic_cast