diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 8ec327600f..bacdb5b0b7 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_wasapi2.zip - URL_MD5 272b27bd6c211c45c0c23d4701b63b5e + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi3.zip + URL_MD5 1a2433f80a788a54c70f505ff4f43ac1 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 1164a0f01e..efcf14fc89 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -59,21 +59,27 @@ Window { } addressLine.text = targetString; toggleOrGo(true); + clearAddressLineTimer.start(); } property var allStories: []; property int cardWidth: 200; property int cardHeight: 152; property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/"; + property bool isCursorVisible: false // Override default cursor visibility. AddressBarDialog { id: addressBarDialog implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height + // 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. + onMetaverseServerUrlChanged: updateLocationTextTimer.start(); + ListModel { id: suggestions } ListView { @@ -185,7 +191,7 @@ Window { color: "gray"; clip: true; anchors.fill: addressLine; - visible: !addressLine.activeFocus; + visible: addressLine.text.length === 0 } TextInput { id: addressLine @@ -201,8 +207,47 @@ Window { bottomMargin: parent.inputAreaStep } font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75 - onTextChanged: filterChoicesByText() - onActiveFocusChanged: updateLocationText(focus) + cursorVisible: false + onTextChanged: { + filterChoicesByText(); + updateLocationText(text.length > 0); + if (!isCursorVisible && text.length > 0) { + isCursorVisible = true; + cursorVisible = true; + } + } + onActiveFocusChanged: { + cursorVisible = isCursorVisible; + } + MouseArea { + // If user clicks in address bar show cursor to indicate ability to enter address. + anchors.fill: parent + onClicked: { + isCursorVisible = true; + parent.cursorVisible = true; + } + } + } + } + + 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; } } @@ -360,9 +405,8 @@ Window { }); } - function updateLocationText(focus) { - addressLine.text = ""; - if (focus) { + function updateLocationText(enteringAddress) { + if (enteringAddress) { notice.text = "Go to a place, @user, path or network address"; notice.color = "gray"; } else { @@ -374,9 +418,9 @@ Window { } onVisibleChanged: { - focus = false; updateLocationText(false); if (visible) { + addressLine.forceActiveFocus(); fillDestinations(); } } @@ -393,11 +437,13 @@ Window { 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/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 3e84c4c3c5..ac566d68c7 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -38,6 +38,7 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare }); _backEnabled = !(DependencyManager::get()->getBackStack().isEmpty()); _forwardEnabled = !(DependencyManager::get()->getForwardStack().isEmpty()); + connect(addressManager.data(), &AddressManager::hostChanged, this, &AddressBarDialog::metaverseServerUrlChanged); connect(DependencyManager::get().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed); connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL); } diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 2dad742ebb..921e808abb 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -37,7 +37,7 @@ signals: void forwardEnabledChanged(); void useFeedChanged(); void receivedHifiSchemeURL(const QString& url); - void metaverseServerUrlChanged(); // While it is a constant, qml will complain about not seeing a change signal. + void metaverseServerUrlChanged(); protected: void displayAddressOfflineMessage(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 656a6bd281..85a8b17361 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -395,9 +395,14 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } }); - if (!updatedEntity) { - return QUuid(); - } + // FIXME: We need to figure out a better way to handle this. Allowing these edits to go through potentially + // breaks avatar energy and entities that are parented. + // + // To handle cases where a script needs to edit an entity with a _known_ entity id but doesn't exist + // in the local entity tree, we need to allow those edits to go through to the server. + // if (!updatedEntity) { + // return QUuid(); + // } _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 80cd30a8b6..f3944f3ea7 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -31,13 +31,28 @@ void AssetScriptingInterface::uploadData(QString data, QScriptValue callback) { QObject::connect(upload, &AssetUpload::finished, this, [this, callback](AssetUpload* upload, const QString& hash) mutable { if (callback.isFunction()) { QString url = "atp:" + hash; - QScriptValueList args { url }; + QScriptValueList args { url, hash }; callback.call(_engine->currentContext()->thisObject(), args); } + upload->deleteLater(); }); upload->start(); } +void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValue callback) { + auto setMappingRequest = DependencyManager::get()->createSetMappingRequest(path, hash); + + QObject::connect(setMappingRequest, &SetMappingRequest::finished, this, [this, callback](SetMappingRequest* request) mutable { + if (callback.isFunction()) { + QScriptValueList args { }; + callback.call(_engine->currentContext()->thisObject(), args); + } + request->deleteLater(); + }); + setMappingRequest->start(); +} + + void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) { const QString ATP_SCHEME { "atp:" }; diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 2c2c596e09..85746ad36e 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -26,6 +26,7 @@ public: Q_INVOKABLE void uploadData(QString data, QScriptValue callback); Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete); + Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback); protected: QSet _pendingRequests; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 160ad77197..18d7b0ebf1 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -106,6 +107,25 @@ void inputControllerFromScriptValue(const QScriptValue &object, controller::Inpu out = qobject_cast(object.toQObject()); } +// The purpose of the following two function is to embed entity ids into entity script filenames +// so that they show up in stacktraces +// +// Extract the url portion of a url that has been encoded with encodeEntityIdIntoEntityUrl(...) +QString extractUrlFromEntityUrl(const QString& url) { + auto parts = url.split(' ', QString::SkipEmptyParts); + if (parts.length() > 0) { + return parts[0]; + } else { + return ""; + } +} + +// Encode an entity id into an entity url +// Example: http://www.example.com/some/path.js [EntityID:{9fdd355f-d226-4887-9484-44432d29520e}] +QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) { + return url + " [EntityID:" + entityID + "]"; +} + static bool hasCorrectSyntax(const QScriptProgram& program) { const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { @@ -1091,14 +1111,19 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { return expandScriptUrl(url); } + QScriptContextInfo contextInfo { currentContext()->parentContext() }; + // we apparently weren't a fully qualified url, so, let's assume we're relative // to the original URL of our script - QUrl parentURL; - if (_parentURL.isEmpty()) { - parentURL = QUrl(_fileNameString); - } else { - parentURL = QUrl(_parentURL); + QUrl parentURL = extractUrlFromEntityUrl(contextInfo.fileName()); + if (parentURL.isEmpty()) { + if (_parentURL.isEmpty()) { + parentURL = QUrl(_fileNameString); + } else { + parentURL = QUrl(_parentURL); + } } + // if the parent URL's scheme is empty, then this is probably a local file... if (parentURL.scheme().isEmpty()) { parentURL = QUrl::fromLocalFile(_fileNameString); @@ -1323,7 +1348,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co auto scriptCache = DependencyManager::get(); bool isFileUrl = isURL && scriptOrURL.startsWith("file://"); - auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); + auto fileName = isURL ? encodeEntityIdIntoEntityUrl(scriptOrURL, entityID.toString()) : "EmbeddedEntityScript"; QScriptProgram program(contents, fileName); if (!hasCorrectSyntax(program)) { diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 05edcdad03..2d3aff44fc 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1002,6 +1002,9 @@ function MyController(hand) { this.secondaryPress = function(value) { _this.rawSecondaryValue = value; + if (value > 0) { + _this.release(); + } }; this.updateSmoothedTrigger = function() { @@ -1849,7 +1852,7 @@ function MyController(hand) { z: 0 }; - var DROP_ANGLE = Math.PI / 6; + var DROP_ANGLE = Math.PI / 3; var HYSTERESIS_FACTOR = 1.1; var ROTATION_ENTER_THRESHOLD = Math.cos(DROP_ANGLE); var ROTATION_EXIT_THRESHOLD = Math.cos(DROP_ANGLE * HYSTERESIS_FACTOR);