diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index b04d55b9eb..3ed7d02364 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -908,31 +908,11 @@ function validateInputs() { } _.each(tables, function(table) { - var inputs = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ':not([data-category]) input[data-changed="true"]'); - - var empty = false; - - _.each(inputs, function(input){ - var inputVal = $(input).val(); - - if (inputVal.length === 0) { - empty = true - - markParentRowInvalid(input); - return; - } - }); - - if (empty) { - showErrorMessage("Error", "Empty field(s)"); - inputsValid = false; - return - } - // validate keys specificially for spaces and equality to an existing key var newKeys = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ' td.key'); var keyWithSpaces = false; + var empty = false; var duplicateKey = false; _.each(newKeys, function(keyCell) { @@ -944,6 +924,14 @@ function validateInputs() { return; } + // make sure the key isn't empty + if (keyVal.length === 0) { + empty = true + + markParentRowInvalid(input); + return; + } + // make sure we don't have duplicate keys in the table var otherKeys = $(table).find('td.key').not(keyCell); _.each(otherKeys, function(otherKeyCell) { @@ -971,6 +959,12 @@ function validateInputs() { return } + if (empty) { + showErrorMessage("Error", "Empty field(s)"); + inputsValid = false; + return + } + if (duplicateKey) { showErrorMessage("Error", "Two keys cannot be identical"); inputsValid = false; diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 7478ca9c2b..b55b9c517d 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -34,6 +34,7 @@ Item { property bool isMyCard: false property bool selected: false property bool isAdmin: false + property bool currentlyEditingDisplayName: false /* User image commented out for now - will probably be re-introduced later. Column { @@ -104,6 +105,7 @@ Item { focus = false myDisplayName.border.width = 0 color = hifi.colors.darkGray + currentlyEditingDisplayName = false } } MouseArea { @@ -115,10 +117,12 @@ Item { myDisplayNameText.focus ? myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX, mouseY, TextInput.CursorOnCharacter) : myDisplayNameText.selectAll(); myDisplayNameText.focus = true myDisplayNameText.color = "black" + currentlyEditingDisplayName = true } onDoubleClicked: { myDisplayNameText.selectAll(); myDisplayNameText.focus = true; + currentlyEditingDisplayName = true } onEntered: myDisplayName.color = hifi.colors.lightGrayText onExited: myDisplayName.color = hifi.colors.textFieldLightBackground diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index f3554727dd..923b09b9ef 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -51,6 +51,7 @@ Rectangle { // This is the container for the PAL Rectangle { + property bool punctuationMode: false id: palContainer // Size width: pal.width - 50 @@ -421,6 +422,16 @@ Rectangle { onExited: adminHelpText.color = hifi.colors.redHighlight } } + HifiControls.Keyboard { + id: keyboard + raised: myCard.currentlyEditingDisplayName && HMD.active + numeric: parent.punctuationMode + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + } } // Timer used when selecting table rows that aren't yet present in the model // (i.e. when selecting avatars using edit.js or sphere overlays) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0217136a0c..af3a449114 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1202,8 +1202,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto entityScriptingInterface = DependencyManager::get(); connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, [this](const EntityItemID& entityItemID, const PointerEvent& event) { - setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); - setKeyboardFocusEntity(entityItemID); + auto entity = getEntities()->getTree()->findEntityByID(entityItemID); + if (entity && entity->wantsKeyboardFocus()) { + setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); + setKeyboardFocusEntity(entityItemID); + } }); connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [=](const EntityItemID& entityItemID) { @@ -4165,6 +4168,8 @@ void Application::setKeyboardFocusOverlay(unsigned int overlayID) { auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR; const float OVERLAY_DEPTH = 0.0105f; setKeyboardFocusHighlight(overlay->getPosition(), overlay->getRotation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH)); + } else if (_keyboardFocusHighlight) { + _keyboardFocusHighlight->setVisible(false); } } } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index f70537a952..a0f7c4e824 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -18,7 +18,7 @@ QString const ModelOverlay::TYPE = "model"; ModelOverlay::ModelOverlay() - : _model(std::make_shared(std::make_shared())), + : _model(std::make_shared(std::make_shared(), nullptr, this)), _modelTextures(QVariantMap()) { _model->init(); @@ -27,7 +27,7 @@ ModelOverlay::ModelOverlay() ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(std::make_shared(std::make_shared())), + _model(std::make_shared(std::make_shared(), nullptr, this)), _modelTextures(QVariantMap()), _url(modelOverlay->_url), _updateModel(false) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 60bb29f85f..1265aabbf2 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -540,7 +540,7 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loadingPriority) { +ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loadingPriority, SpatiallyNestable* spatiallyNestableOverride) { ModelPointer model = nullptr; // Only create and delete models on the thread that owns the EntityTreeRenderer @@ -552,7 +552,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loading return model; } - model = std::make_shared(std::make_shared()); + model = std::make_shared(std::make_shared(), nullptr, spatiallyNestableOverride); model->setLoadingPriority(loadingPriority); model->init(); model->setURL(QUrl(url)); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 29d463b915..8669a1c4d3 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -77,7 +77,7 @@ public: void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE ModelPointer allocateModel(const QString& url, float loadingPriority = 0.0f); + Q_INVOKABLE ModelPointer allocateModel(const QString& url, float loadingPriority = 0.0f, SpatiallyNestable* spatiallyNestableOverride = nullptr); /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index bc8c7c222e..e6902228c5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -504,8 +504,7 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointerallocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this)); - _model->setSpatiallyNestableOverride(shared_from_this()); + _model = _myRenderer->allocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this), this); _needsInitialSimulation = true; // If we need to change URLs, update it *after rendering* (to avoid access violations) } else if (QUrl(getModelURL()) != _model->getURL()) { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3c10d0382c..353819e01d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -651,6 +651,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data // even when we would otherwise ignore the rest of the packet. + bool filterRejection = false; if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) { QByteArray simOwnerData; @@ -663,6 +664,10 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef if (wantTerseEditLogging() && _simulationOwner != newSimOwner) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner; } + // This is used in the custom physics setters, below. When an entity-server filter alters + // or rejects a set of properties, it clears this. In such cases, we don't want those custom + // setters to ignore what the server says. + filterRejection = newSimOwner.getID().isNull(); if (weOwnSimulation) { if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) { // entity-server is trying to clear our ownership (probably at our own request) @@ -715,55 +720,45 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // Note: duplicate packets are expected and not wrong. They may be sent for any number of // reasons and the contract is that the client handles them in an idempotent manner. auto lastEdited = lastEditedFromBufferAdjusted; - auto customUpdatePositionFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){ - bool simulationChanged = lastEdited > _lastUpdatedPositionTimestamp; - bool valueChanged = value != _lastUpdatedPositionValue; - bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged; - if (shouldUpdate) { + bool otherOverwrites = overwriteLocalData && !weOwnSimulation; + auto shouldUpdate = [lastEdited, otherOverwrites, filterRejection](quint64 updatedTimestamp, bool valueChanged) { + bool simulationChanged = lastEdited > updatedTimestamp; + return otherOverwrites && simulationChanged && (valueChanged || filterRejection); + }; + auto customUpdatePositionFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value){ + if (shouldUpdate(_lastUpdatedPositionTimestamp, value != _lastUpdatedPositionValue)) { updatePositionFromNetwork(value); _lastUpdatedPositionTimestamp = lastEdited; _lastUpdatedPositionValue = value; } }; - auto customUpdateRotationFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::quat value){ - bool simulationChanged = lastEdited > _lastUpdatedRotationTimestamp; - bool valueChanged = value != _lastUpdatedRotationValue; - bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged; - if (shouldUpdate) { + auto customUpdateRotationFromNetwork = [this, shouldUpdate, lastEdited](glm::quat value){ + if (shouldUpdate(_lastUpdatedRotationTimestamp, value != _lastUpdatedRotationValue)) { updateRotationFromNetwork(value); _lastUpdatedRotationTimestamp = lastEdited; _lastUpdatedRotationValue = value; } }; - auto customUpdateVelocityFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){ - bool simulationChanged = lastEdited > _lastUpdatedVelocityTimestamp; - bool valueChanged = value != _lastUpdatedVelocityValue; - bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged; - if (shouldUpdate) { + auto customUpdateVelocityFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value){ + if (shouldUpdate(_lastUpdatedVelocityTimestamp, value != _lastUpdatedVelocityValue)) { updateVelocityFromNetwork(value); _lastUpdatedVelocityTimestamp = lastEdited; _lastUpdatedVelocityValue = value; } }; - auto customUpdateAngularVelocityFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){ - bool simulationChanged = lastEdited > _lastUpdatedAngularVelocityTimestamp; - bool valueChanged = value != _lastUpdatedAngularVelocityValue; - bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged; - if (shouldUpdate) { + auto customUpdateAngularVelocityFromNetwork = [this, shouldUpdate, lastEdited](glm::vec3 value){ + if (shouldUpdate(_lastUpdatedAngularVelocityTimestamp, value != _lastUpdatedAngularVelocityValue)) { updateAngularVelocityFromNetwork(value); _lastUpdatedAngularVelocityTimestamp = lastEdited; _lastUpdatedAngularVelocityValue = value; } }; - auto customSetAcceleration = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){ - bool simulationChanged = lastEdited > _lastUpdatedAccelerationTimestamp; - bool valueChanged = value != _lastUpdatedAccelerationValue; - bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged; - if (shouldUpdate) { + auto customSetAcceleration = [this, shouldUpdate, lastEdited](glm::vec3 value){ + if (shouldUpdate(_lastUpdatedAccelerationTimestamp, value != _lastUpdatedAccelerationValue)) { setAcceleration(value); _lastUpdatedAccelerationTimestamp = lastEdited; _lastUpdatedAccelerationValue = value; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 39371cc3e3..3308ba36ab 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -73,6 +73,9 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event) { _mousePressTime = usecTimestampNow(); _mouseMoved = false; + _mousePressPos = event->pos(); + _clickDeadspotActive = true; + eraseMouseClicked(); } @@ -84,9 +87,11 @@ void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event) { // input for this button we might want to add some small tolerance to this so if you do a small drag it // still counts as a click. static const int CLICK_TIME = USECS_PER_MSEC * 500; // 500 ms to click - if (!_mouseMoved && (usecTimestampNow() - _mousePressTime < CLICK_TIME)) { + if (_clickDeadspotActive && (usecTimestampNow() - _mousePressTime < CLICK_TIME)) { _inputDevice->_buttonPressedMap.insert(_inputDevice->makeInput((Qt::MouseButton) event->button(), true).getChannel()); } + + _clickDeadspotActive = false; } void KeyboardMouseDevice::eraseMouseClicked() { @@ -109,9 +114,14 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event) { // outside of the application window, because we don't get MouseEvents when the cursor is outside // of the application window. _lastCursor = currentPos; + _mouseMoved = true; - eraseMouseClicked(); + const int CLICK_EVENT_DEADSPOT = 6; // pixels + if (_clickDeadspotActive && (_mousePressPos - currentPos).manhattanLength() > CLICK_EVENT_DEADSPOT) { + eraseMouseClicked(); + _clickDeadspotActive = false; + } } void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 399ca4e93d..f38b43c107 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -118,8 +118,10 @@ public: protected: QPoint _lastCursor; + QPoint _mousePressPos; quint64 _mousePressTime; bool _mouseMoved; + bool _clickDeadspotActive; glm::vec2 _lastTouch; std::shared_ptr _inputDevice { std::make_shared() }; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 018a7e6954..e1627f2fd6 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -78,11 +78,12 @@ void initCollisionMaterials() { } } -Model::Model(RigPointer rig, QObject* parent) : +Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : QObject(parent), _renderGeometry(), _collisionGeometry(), _renderWatcher(_renderGeometry), + _spatiallyNestableOverride(spatiallyNestableOverride), _translation(0.0f), _rotation(), _scale(1.0f, 1.0f, 1.0f), @@ -133,16 +134,10 @@ void Model::setRotation(const glm::quat& rotation) { updateRenderItems(); } -void Model::setSpatiallyNestableOverride(SpatiallyNestablePointer override) { - _spatiallyNestableOverride = override; - updateRenderItems(); -} - Transform Model::getTransform() const { - SpatiallyNestablePointer spatiallyNestableOverride = _spatiallyNestableOverride.lock(); - if (spatiallyNestableOverride) { + if (_spatiallyNestableOverride) { bool success; - Transform transform = spatiallyNestableOverride->getTransform(success); + Transform transform = _spatiallyNestableOverride->getTransform(success); if (success) { transform.setScale(getScale()); return transform; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 49890bfb04..2042a16801 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -67,7 +67,7 @@ public: static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; } - Model(RigPointer rig, QObject* parent = nullptr); + Model(RigPointer rig, QObject* parent = nullptr, SpatiallyNestable* spatiallyNestableOverride = nullptr); virtual ~Model(); inline ModelPointer getThisPointer() const { @@ -205,7 +205,6 @@ public: void setTranslation(const glm::vec3& translation); void setRotation(const glm::quat& rotation); - void setSpatiallyNestableOverride(SpatiallyNestablePointer ptr); const glm::vec3& getTranslation() const { return _translation; } const glm::quat& getRotation() const { return _rotation; } @@ -292,12 +291,11 @@ protected: GeometryResourceWatcher _renderWatcher; + SpatiallyNestable* _spatiallyNestableOverride; + glm::vec3 _translation; glm::quat _rotation; glm::vec3 _scale; - - SpatiallyNestableWeakPointer _spatiallyNestableOverride; - glm::vec3 _offset; static float FAKE_DIMENSION_PLACEHOLDER; diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index bb96fe96cf..36b0ddde85 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -24,7 +24,7 @@ var DEFAULT_SCRIPTS = [ "system/goto.js", "system/marketplaces/marketplaces.js", "system/edit.js", - "system/users.js", + "system/tablet-users.js", "system/selectAudioDevice.js", "system/notifications.js", "system/controllers/controllerDisplayManager.js", diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 972d95e9e9..86820c990a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -853,7 +853,7 @@ function MyController(hand) { }; this.setState = function(newState, reason) { - if (isInEditMode() && (newState !== STATE_OFF && + if ((isInEditMode() && this.grabbedEntity !== HMD.tabletID )&& (newState !== STATE_OFF && newState !== STATE_SEARCHING && newState !== STATE_OVERLAY_STYLUS_TOUCHING)) { return; @@ -1703,7 +1703,7 @@ function MyController(hand) { }; this.isTablet = function (entityID) { - if (entityID === HMD.tabletID) { // XXX what's a better way to know this? + if (entityID === HMD.tabletID) { return true; } return false; diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html new file mode 100644 index 0000000000..c18162ff8a --- /dev/null +++ b/scripts/system/html/users.html @@ -0,0 +1,465 @@ + + + + Users Online + + + + + + + +
+
+
Users Online
+ +
+
+
+
+

+ +
+
    +
  • Everyone (0)
  • +
  • Friends (0)
  • +
+
+
    +
    +
    +
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 2932417d25..b9bae72d14 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1038,7 +1038,7 @@ SelectionDisplay = (function() { if (entityIntersection.intersects && (!overlayIntersection.intersects || (entityIntersection.distance < overlayIntersection.distance))) { - if (HMD.tabletID == entityIntersection.entityID) { + if (HMD.tabletID === entityIntersection.entityID) { return; } diff --git a/scripts/system/tablet-users.js b/scripts/system/tablet-users.js new file mode 100644 index 0000000000..0fad0c56f3 --- /dev/null +++ b/scripts/system/tablet-users.js @@ -0,0 +1,98 @@ +"use strict"; + +// +// users.js +// +// Created by Faye Li on 18 Jan 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + var USERS_URL = "https://hifi-content.s3.amazonaws.com/faye/tablet-dev/users.html"; + + var FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends"; + var FRIENDS_WINDOW_WIDTH = 290; + var FRIENDS_WINDOW_HEIGHT = 500; + var FRIENDS_WINDOW_TITLE = "Add/Remove Friends"; + + // Initialise visibility based on global service + var VISIBILITY_VALUES_SET = {}; + VISIBILITY_VALUES_SET["all"] = true; + VISIBILITY_VALUES_SET["friends"] = true; + VISIBILITY_VALUES_SET["none"] = true; + var myVisibility; + if (GlobalServices.findableBy in VISIBILITY_VALUES_SET) { + myVisibility = GlobalServices.findableBy; + } else { + // default to friends if it can't be determined + myVisibility = "friends"; + GlobalServices.findableBy = myVisibilty; + } + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + icon: "icons/tablet-icons/people-i.svg", + text: "Users" + }); + + function onClicked() { + tablet.gotoWebScreen(USERS_URL); + } + + function onWebEventReceived(event) { + print("Script received a web event, its type is " + typeof event); + if (typeof event === "string") { + event = JSON.parse(event); + } + if (event.type === "ready") { + // send username to html + var myUsername = GlobalServices.username; + var object = { + "type": "user-info", + "data": { + "username": myUsername, + "visibility": myVisibility + } + }; + tablet.emitScriptEvent(JSON.stringify(object)); + } + if (event.type === "manage-friends") { + // open a web overlay to metaverse friends page + var friendsWindow = new OverlayWebWindow({ + title: FRIENDS_WINDOW_TITLE, + width: FRIENDS_WINDOW_WIDTH, + height: FRIENDS_WINDOW_HEIGHT, + visible: false + }); + friendsWindow.setURL(FRIENDS_WINDOW_URL); + friendsWindow.setVisible(true); + friendsWindow.raise(); + } + if (event.type === "jump-to") { + if (typeof event.data.username !== undefined) { + // teleport to selected user from the online users list + location.goToUser(event.data.username); + } + } + if (event.type === "toggle-visibility") { + if (typeof event.data.visibility !== undefined) { + // update your visibility (all, friends, or none) + myVisibility = event.data.visibility; + GlobalServices.findableBy = myVisibility; + } + } + } + + button.clicked.connect(onClicked); + tablet.webEventReceived.connect(onWebEventReceived); + + function cleanup() { + button.clicked.disconnect(onClicked); + tablet.removeButton(button); + } + + Script.scriptEnding.connect(cleanup); +}()); // END LOCAL_SCOPE