From 4335bbe4fe7384efedca288c257ea50882c1034d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 24 Jan 2017 18:48:11 -0800 Subject: [PATCH 1/4] provide a way to switch mostly back to HUD-based ui --- scripts/defaultScriptsHUD.js | 131 ++ scripts/system/bubbleHUD.js | 195 +++ scripts/system/gotoHUD.js | 46 + scripts/system/helpHUD.js | 41 + scripts/system/hmdHUD.js | 79 ++ scripts/system/marketplaces/marketplaceHUD.js | 131 ++ .../system/marketplaces/marketplacesHUD.js | 233 ++++ scripts/system/muteHUD.js | 51 + scripts/system/palHUD.js | 658 +++++++++ scripts/system/snapshotHUD.js | 226 +++ scripts/system/usersHUD.js | 1237 +++++++++++++++++ 11 files changed, 3028 insertions(+) create mode 100644 scripts/defaultScriptsHUD.js create mode 100644 scripts/system/bubbleHUD.js create mode 100644 scripts/system/gotoHUD.js create mode 100644 scripts/system/helpHUD.js create mode 100644 scripts/system/hmdHUD.js create mode 100644 scripts/system/marketplaces/marketplaceHUD.js create mode 100644 scripts/system/marketplaces/marketplacesHUD.js create mode 100644 scripts/system/muteHUD.js create mode 100644 scripts/system/palHUD.js create mode 100644 scripts/system/snapshotHUD.js create mode 100644 scripts/system/usersHUD.js diff --git a/scripts/defaultScriptsHUD.js b/scripts/defaultScriptsHUD.js new file mode 100644 index 0000000000..098a74ffd0 --- /dev/null +++ b/scripts/defaultScriptsHUD.js @@ -0,0 +1,131 @@ +"use strict"; +/* jslint vars: true, plusplus: true */ + +// +// defaultScripts.js +// examples +// +// Copyright 2014 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 +// + +var DEFAULT_SCRIPTS = [ + "system/progress.js", + "system/away.js", + "system/usersHUD.js", + "system/muteHUD.js", + "system/gotoHUD.js", + "system/hmdHUD.js", + "system/marketplaces/marketplacesHUD.js", + "system/edit.js", + "system/palHUD.js", //"system/mod.js", // older UX, if you prefer + "system/selectAudioDevice.js", + "system/notifications.js", + "system/controllers/controllerDisplayManager.js", + "system/controllers/handControllerGrab.js", + "system/controllers/handControllerPointer.js", + "system/controllers/squeezeHands.js", + "system/controllers/grab.js", + "system/controllers/teleport.js", + "system/controllers/toggleAdvancedMovementForHandControllers.js", + "system/dialTone.js", + "system/firstPersonHMD.js", + "system/snapshotHUD.js", + "system/helpHUD.js", + "system/bubbleHUD.js" +]; + +// add a menu item for debugging +var MENU_CATEGORY = "Developer"; +var MENU_ITEM = "Debug defaultScripts.js"; + +var SETTINGS_KEY = '_debugDefaultScriptsIsChecked'; +var previousSetting = Settings.getValue(SETTINGS_KEY); + +if (previousSetting === '' || previousSetting === false || previousSetting === 'false') { + previousSetting = false; +} + +if (previousSetting === true || previousSetting === 'true') { + previousSetting = true; +} + + + + +if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_ITEM)) { + Menu.addMenuItem({ + menuName: MENU_CATEGORY, + menuItemName: MENU_ITEM, + isCheckable: true, + isChecked: previousSetting, + grouping: "Advanced" + }); +} + +function runDefaultsTogether() { + for (var j in DEFAULT_SCRIPTS) { + Script.include(DEFAULT_SCRIPTS[j]); + } +} + +function runDefaultsSeparately() { + for (var i in DEFAULT_SCRIPTS) { + Script.load(DEFAULT_SCRIPTS[i]); + } +} +// start all scripts +if (Menu.isOptionChecked(MENU_ITEM)) { + // we're debugging individual default scripts + // so we load each into its own ScriptEngine instance + debuggingDefaultScripts = true; + runDefaultsSeparately(); +} else { + // include all default scripts into this ScriptEngine + runDefaultsTogether(); +} + +function menuItemEvent(menuItem) { + if (menuItem == MENU_ITEM) { + + isChecked = Menu.isOptionChecked(MENU_ITEM); + if (isChecked === true) { + Settings.setValue(SETTINGS_KEY, true); + } else if (isChecked === false) { + Settings.setValue(SETTINGS_KEY, false); + } + Window.alert('You must reload all scripts for this to take effect.') + } + + +} + + + +function stopLoadedScripts() { + // remove debug script loads + var runningScripts = ScriptDiscoveryService.getRunning(); + for (var i in runningScripts) { + var scriptName = runningScripts[i].name; + for (var j in DEFAULT_SCRIPTS) { + if (DEFAULT_SCRIPTS[j].slice(-scriptName.length) === scriptName) { + ScriptDiscoveryService.stopScript(runningScripts[i].url); + } + } + } +} + +function removeMenuItem() { + if (!Menu.isOptionChecked(MENU_ITEM)) { + Menu.removeMenuItem(MENU_CATEGORY, MENU_ITEM); + } +} + +Script.scriptEnding.connect(function() { + stopLoadedScripts(); + removeMenuItem(); +}); + +Menu.menuItemEvent.connect(menuItemEvent); diff --git a/scripts/system/bubbleHUD.js b/scripts/system/bubbleHUD.js new file mode 100644 index 0000000000..2f7286872e --- /dev/null +++ b/scripts/system/bubbleHUD.js @@ -0,0 +1,195 @@ +"use strict"; + +// +// bubble.js +// scripts/system/ +// +// Created by Brad Hefta-Gaub on 11/18/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/* global Toolbars, Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */ + + +(function () { // BEGIN LOCAL_SCOPE + + // grab the toolbar + var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + // Used for animating and disappearing the bubble + var bubbleOverlayTimestamp; + // Used for flashing the HUD button upon activation + var bubbleButtonFlashState = false; + // Used for flashing the HUD button upon activation + var bubbleButtonTimestamp; + // Affects bubble height + const BUBBLE_HEIGHT_SCALE = 0.15; + // The bubble model itself + var bubbleOverlay = Overlays.addOverlay("model", { + url: Script.resolvePath("assets/models/Bubble-v14.fbx"), // If you'd like to change the model, modify this line (and the dimensions below) + dimensions: { x: 1.0, y: 0.75, z: 1.0 }, + position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z }, + rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), + scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 }, + visible: false, + ignoreRayIntersection: true + }); + // The bubble activation sound + var bubbleActivateSound = SoundCache.getSound(Script.resolvePath("assets/sounds/bubble.wav")); + // Is the update() function connected? + var updateConnected = false; + + const BUBBLE_VISIBLE_DURATION_MS = 3000; + const BUBBLE_RAISE_ANIMATION_DURATION_MS = 750; + const BUBBLE_HUD_ICON_FLASH_INTERVAL_MS = 500; + + var ASSETS_PATH = Script.resolvePath("assets"); + var TOOLS_PATH = Script.resolvePath("assets/images/tools/"); + + function buttonImageURL() { + return TOOLS_PATH + 'bubble.svg'; + } + + // Hides the bubble model overlay and resets the button flash state + function hideOverlays() { + Overlays.editOverlay(bubbleOverlay, { + visible: false + }); + bubbleButtonFlashState = false; + } + + // Make the bubble overlay visible, set its position, and play the sound + function createOverlays() { + Audio.playSound(bubbleActivateSound, { + position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }, + localOnly: true, + volume: 0.2 + }); + hideOverlays(); + if (updateConnected === true) { + updateConnected = false; + Script.update.disconnect(update); + } + + Overlays.editOverlay(bubbleOverlay, { + position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z }, + rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), + scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 }, + visible: true + }); + bubbleOverlayTimestamp = Date.now(); + bubbleButtonTimestamp = bubbleOverlayTimestamp; + Script.update.connect(update); + updateConnected = true; + } + + // Called from the C++ scripting interface to show the bubble overlay + function enteredIgnoreRadius() { + createOverlays(); + } + + // Used to set the state of the bubble HUD button + function writeButtonProperties(parameter) { + button.writeProperty('buttonState', parameter ? 0 : 1); + button.writeProperty('defaultState', parameter ? 0 : 1); + button.writeProperty('hoverState', parameter ? 2 : 3); + } + + // The bubble script's update function + update = function () { + var timestamp = Date.now(); + var delay = (timestamp - bubbleOverlayTimestamp); + var overlayAlpha = 1.0 - (delay / BUBBLE_VISIBLE_DURATION_MS); + if (overlayAlpha > 0) { + // Flash button + if ((timestamp - bubbleButtonTimestamp) >= BUBBLE_VISIBLE_DURATION_MS) { + writeButtonProperties(bubbleButtonFlashState); + bubbleButtonTimestamp = timestamp; + bubbleButtonFlashState = !bubbleButtonFlashState; + } + + if (delay < BUBBLE_RAISE_ANIMATION_DURATION_MS) { + Overlays.editOverlay(bubbleOverlay, { + // Quickly raise the bubble from the ground up + position: { + x: MyAvatar.position.x, + y: (-((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, + z: MyAvatar.position.z + }, + rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), + scale: { + x: 2, + y: ((1 - ((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 0.5 + 0.5), + z: 2 + } + }); + } else { + // Keep the bubble in place for a couple seconds + Overlays.editOverlay(bubbleOverlay, { + position: { + x: MyAvatar.position.x, + y: MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, + z: MyAvatar.position.z + }, + rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), + scale: { + x: 2, + y: MyAvatar.scale * 0.5 + 0.5, + z: 2 + } + }); + } + } else { + hideOverlays(); + if (updateConnected === true) { + Script.update.disconnect(update); + updateConnected = false; + } + var bubbleActive = Users.getIgnoreRadiusEnabled(); + writeButtonProperties(bubbleActive); + } + }; + + // When the space bubble is toggled... + function onBubbleToggled() { + var bubbleActive = Users.getIgnoreRadiusEnabled(); + writeButtonProperties(bubbleActive); + if (bubbleActive) { + createOverlays(); + } else { + hideOverlays(); + if (updateConnected === true) { + Script.update.disconnect(update); + updateConnected = false; + } + } + } + + // Setup the bubble button and add it to the toolbar + var button = toolbar.addButton({ + objectName: 'bubble', + imageURL: buttonImageURL(), + visible: true, + alpha: 0.9 + }); + onBubbleToggled(); + + button.clicked.connect(Users.toggleIgnoreRadius); + Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled); + Users.enteredIgnoreRadius.connect(enteredIgnoreRadius); + + // Cleanup the toolbar button and overlays when script is stopped + Script.scriptEnding.connect(function () { + toolbar.removeButton('bubble'); + button.clicked.disconnect(Users.toggleIgnoreRadius); + Users.ignoreRadiusEnabledChanged.disconnect(onBubbleToggled); + Users.enteredIgnoreRadius.disconnect(enteredIgnoreRadius); + Overlays.deleteOverlay(bubbleOverlay); + bubbleButtonFlashState = false; + if (updateConnected === true) { + Script.update.disconnect(update); + } + }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/gotoHUD.js b/scripts/system/gotoHUD.js new file mode 100644 index 0000000000..9116142293 --- /dev/null +++ b/scripts/system/gotoHUD.js @@ -0,0 +1,46 @@ +"use strict"; + +// +// goto.js +// scripts/system/ +// +// Created by Howard Stearns on 2 Jun 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + + +var button = toolBar.addButton({ + objectName: "goto", + imageURL: Script.resolvePath("assets/images/tools/directory.svg"), + visible: true, + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9, +}); + +function onAddressBarShown(visible) { + button.writeProperty('buttonState', visible ? 0 : 1); + button.writeProperty('defaultState', visible ? 0 : 1); + button.writeProperty('hoverState', visible ? 2 : 3); +} +function onClicked(){ + DialogsManager.toggleAddressBar(); +} +button.clicked.connect(onClicked); +DialogsManager.addressBarShown.connect(onAddressBarShown); + +Script.scriptEnding.connect(function () { + toolBar.removeButton("goto"); + button.clicked.disconnect(onClicked); + DialogsManager.addressBarShown.disconnect(onAddressBarShown); +}); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/helpHUD.js b/scripts/system/helpHUD.js new file mode 100644 index 0000000000..e79ed0444c --- /dev/null +++ b/scripts/system/helpHUD.js @@ -0,0 +1,41 @@ +"use strict"; + +// +// help.js +// scripts/system/ +// +// Created by Howard Stearns on 2 Nov 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + + var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + var buttonName = "help"; // matching location reserved in Desktop.qml + var button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/help.svg"), + visible: true, + hoverState: 2, + defaultState: 1, + buttonState: 1, + alpha: 0.9 + }); + + // TODO: make button state reflect whether the window is opened or closed (independently from us). + + function onClicked(){ + Menu.triggerOption('Help...') + } + + button.clicked.connect(onClicked); + + Script.scriptEnding.connect(function () { + toolBar.removeButton(buttonName); + button.clicked.disconnect(onClicked); + }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/hmdHUD.js b/scripts/system/hmdHUD.js new file mode 100644 index 0000000000..5dd06de8eb --- /dev/null +++ b/scripts/system/hmdHUD.js @@ -0,0 +1,79 @@ +"use strict"; + +// +// hmd.js +// scripts/system/ +// +// Created by Howard Stearns on 2 Jun 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + +var headset; // The preferred headset. Default to the first one found in the following list. +var displayMenuName = "Display"; +var desktopMenuItemName = "Desktop"; +['OpenVR (Vive)', 'Oculus Rift'].forEach(function (name) { + if (!headset && Menu.menuItemExists(displayMenuName, name)) { + headset = name; + } +}); + +var controllerDisplay = false; +function updateControllerDisplay() { + if (HMD.active && Menu.isOptionChecked("Third Person")) { + if (!controllerDisplay) { + HMD.requestShowHandControllers(); + controllerDisplay = true; + } + } else if (controllerDisplay) { + HMD.requestHideHandControllers(); + controllerDisplay = false; + } +} + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +var button; +// Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through. +// Disable them in hmd. +var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode']; +function onHmdChanged(isHmd) { + button.writeProperty('buttonState', isHmd ? 0 : 1); + button.writeProperty('defaultState', isHmd ? 0 : 1); + button.writeProperty('hoverState', isHmd ? 2 : 3); + desktopOnlyViews.forEach(function (view) { + Menu.setMenuEnabled("View>" + view, !isHmd); + }); + updateControllerDisplay(); +} +function onClicked(){ + var isDesktop = Menu.isOptionChecked(desktopMenuItemName); + Menu.setIsOptionChecked(isDesktop ? headset : desktopMenuItemName, true); +} +if (headset) { + button = toolBar.addButton({ + objectName: "hmdToggle", + imageURL: Script.resolvePath("assets/images/tools/switch.svg"), + visible: true, + hoverState: 2, + defaultState: 0, + alpha: 0.9 + }); + onHmdChanged(HMD.active); + + button.clicked.connect(onClicked); + HMD.displayModeChanged.connect(onHmdChanged); + Camera.modeUpdated.connect(updateControllerDisplay); + + Script.scriptEnding.connect(function () { + toolBar.removeButton("hmdToggle"); + button.clicked.disconnect(onClicked); + HMD.displayModeChanged.disconnect(onHmdChanged); + Camera.modeUpdated.disconnect(updateControllerDisplay); + }); +} + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/marketplaces/marketplaceHUD.js b/scripts/system/marketplaces/marketplaceHUD.js new file mode 100644 index 0000000000..894dae7eac --- /dev/null +++ b/scripts/system/marketplaces/marketplaceHUD.js @@ -0,0 +1,131 @@ +// +// marketplace.js +// +// Created by Eric Levin on 8 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + +/* global WebTablet */ +Script.include("../libraries/WebTablet.js"); + +var toolIconUrl = Script.resolvePath("../assets/images/tools/"); + +var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; +var marketplaceWindow = new OverlayWebWindow({ + title: "Marketplace", + source: "about:blank", + width: 900, + height: 700, + visible: false +}); + +var toolHeight = 50; +var toolWidth = 50; +var TOOLBAR_MARGIN_Y = 0; +var marketplaceVisible = false; +var marketplaceWebTablet; + +// We persist clientOnly data in the .ini file, and reconsistitute it on restart. +// To keep things consistent, we pickle the tablet data in Settings, and kill any existing such on restart and domain change. +var persistenceKey = "io.highfidelity.lastDomainTablet"; + +function shouldShowWebTablet() { + var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); + var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); + var hasHydra = !!Controller.Hardware.Hydra; + return HMD.active && (leftPose.valid || rightPose.valid || hasHydra); +} + +function showMarketplace(marketplaceID) { + if (shouldShowWebTablet()) { + updateButtonState(true); + marketplaceWebTablet = new WebTablet("https://metaverse.highfidelity.com/marketplace", null, null, true); + Settings.setValue(persistenceKey, marketplaceWebTablet.pickle()); + } else { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); + } + + marketplaceVisible = true; + UserActivityLogger.openedMarketplace(); +} + +function hideTablet(tablet) { + if (!tablet) { + return; + } + updateButtonState(false); + tablet.destroy(); + marketplaceWebTablet = null; + Settings.setValue(persistenceKey, ""); +} +function clearOldTablet() { // If there was a tablet from previous domain or session, kill it and let it be recreated + var tablet = WebTablet.unpickle(Settings.getValue(persistenceKey, "")); + hideTablet(tablet); +} +function hideMarketplace() { + if (marketplaceWindow.visible) { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); + } else if (marketplaceWebTablet) { + hideTablet(marketplaceWebTablet); + } + marketplaceVisible = false; +} + +function toggleMarketplace() { + if (marketplaceVisible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + +var browseExamplesButton = toolBar.addButton({ + imageURL: toolIconUrl + "market.svg", + objectName: "marketplace", + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 +}); + +function updateButtonState(visible) { + browseExamplesButton.writeProperty('buttonState', visible ? 0 : 1); + browseExamplesButton.writeProperty('defaultState', visible ? 0 : 1); + browseExamplesButton.writeProperty('hoverState', visible ? 2 : 3); +} +function onMarketplaceWindowVisibilityChanged() { + updateButtonState(marketplaceWindow.visible); + marketplaceVisible = marketplaceWindow.visible; +} + +function onClick() { + toggleMarketplace(); +} + +browseExamplesButton.clicked.connect(onClick); +marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged); + +clearOldTablet(); // Run once at startup, in case there's anything laying around from a crash. +// We could also optionally do something like Window.domainChanged.connect(function () {Script.setTimeout(clearOldTablet, 2000)}), +// but the HUD version stays around, so lets do the same. + +Script.scriptEnding.connect(function () { + toolBar.removeButton("marketplace"); + browseExamplesButton.clicked.disconnect(onClick); + marketplaceWindow.visibleChanged.disconnect(onMarketplaceWindowVisibilityChanged); +}); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/marketplaces/marketplacesHUD.js b/scripts/system/marketplaces/marketplacesHUD.js new file mode 100644 index 0000000000..d5530e7db2 --- /dev/null +++ b/scripts/system/marketplaces/marketplacesHUD.js @@ -0,0 +1,233 @@ +// +// marketplaces.js +// +// Created by Eric Levin on 8 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + +/* global WebTablet */ +Script.include("../libraries/WebTablet.js"); + +var toolIconUrl = Script.resolvePath("../assets/images/tools/"); + +var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; +var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. +var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); +var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); + +// Event bridge messages. +var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; +var CLARA_IO_STATUS = "CLARA.IO STATUS"; +var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; +var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; +var GOTO_DIRECTORY = "GOTO_DIRECTORY"; +var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; +var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; +var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; + +var CLARA_DOWNLOAD_TITLE = "Preparing Download"; +var messageBox = null; +var isDownloadBeingCancelled = false; + +var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel +var NO_BUTTON = 0; // QMessageBox::NoButton + +var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; + +var marketplaceWindow = new OverlayWebWindow({ + title: "Marketplace", + source: "about:blank", + width: 900, + height: 700, + visible: false +}); +marketplaceWindow.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); + +function onWebEventReceived(message) { + if (message === GOTO_DIRECTORY) { + var url = MARKETPLACES_URL; + if (marketplaceWindow.visible) { + marketplaceWindow.setURL(url); + } + if (marketplaceWebTablet) { + marketplaceWebTablet.setURL(url); + } + return; + } + if (message === QUERY_CAN_WRITE_ASSETS) { + var canWriteAssets = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); + if (marketplaceWindow.visible) { + marketplaceWindow.emitScriptEvent(canWriteAssets); + } + if (marketplaceWebTablet) { + marketplaceWebTablet.getOverlayObject().emitScriptEvent(canWriteAssets); + } + return; + } + if (message === WARN_USER_NO_PERMISSIONS) { + Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); + return; + } + + if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { + if (isDownloadBeingCancelled) { + return; + } + + var text = message.slice(CLARA_IO_STATUS.length); + if (messageBox === null) { + messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } else { + Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } + return; + } + + if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { + if (messageBox !== null) { + Window.closeMessageBox(messageBox); + messageBox = null; + } + return; + } + + if (message === CLARA_IO_CANCELLED_DOWNLOAD) { + isDownloadBeingCancelled = false; + } +} + +marketplaceWindow.webEventReceived.connect(onWebEventReceived); + +function onMessageBoxClosed(id, button) { + if (id === messageBox && button === CANCEL_BUTTON) { + isDownloadBeingCancelled = true; + messageBox = null; + marketplaceWindow.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); + } +} + +Window.messageBoxClosed.connect(onMessageBoxClosed); + +var toolHeight = 50; +var toolWidth = 50; +var TOOLBAR_MARGIN_Y = 0; +var marketplaceVisible = false; +var marketplaceWebTablet; + +// We persist clientOnly data in the .ini file, and reconstitute it on restart. +// To keep things consistent, we pickle the tablet data in Settings, and kill any existing such on restart and domain change. +var persistenceKey = "io.highfidelity.lastDomainTablet"; + +function shouldShowWebTablet() { + var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); + var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); + var hasHydra = !!Controller.Hardware.Hydra; + return HMD.active && (leftPose.valid || rightPose.valid || hasHydra); +} + +function showMarketplace() { + if (shouldShowWebTablet()) { + updateButtonState(true); + marketplaceWebTablet = new WebTablet(MARKETPLACE_URL_INITIAL, null, null, true); + Settings.setValue(persistenceKey, marketplaceWebTablet.pickle()); + marketplaceWebTablet.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); + marketplaceWebTablet.getOverlayObject().webEventReceived.connect(onWebEventReceived); + } else { + marketplaceWindow.setURL(MARKETPLACE_URL_INITIAL); + marketplaceWindow.setVisible(true); + } + + marketplaceVisible = true; + UserActivityLogger.openedMarketplace(); +} + +function hideTablet(tablet) { + if (!tablet) { + return; + } + updateButtonState(false); + tablet.destroy(); + marketplaceWebTablet = null; + Settings.setValue(persistenceKey, ""); +} +function clearOldTablet() { // If there was a tablet from previous domain or session, kill it and let it be recreated + var tablet = WebTablet.unpickle(Settings.getValue(persistenceKey, "")); + hideTablet(tablet); +} +function hideMarketplace() { + if (marketplaceWindow.visible) { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); + } else if (marketplaceWebTablet) { + hideTablet(marketplaceWebTablet); + } + marketplaceVisible = false; +} +marketplaceWindow.closed.connect(function () { + marketplaceWindow.setURL("about:blank"); +}); + +function toggleMarketplace() { + if (marketplaceVisible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + +var browseExamplesButton = toolBar.addButton({ + imageURL: toolIconUrl + "market.svg", + objectName: "marketplace", + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 +}); + +function updateButtonState(visible) { + browseExamplesButton.writeProperty('buttonState', visible ? 0 : 1); + browseExamplesButton.writeProperty('defaultState', visible ? 0 : 1); + browseExamplesButton.writeProperty('hoverState', visible ? 2 : 3); +} +function onMarketplaceWindowVisibilityChanged() { + updateButtonState(marketplaceWindow.visible); + marketplaceVisible = marketplaceWindow.visible; +} + +function onCanWriteAssetsChanged() { + var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); + if (marketplaceWindow.visible) { + marketplaceWindow.emitScriptEvent(message); + } + if (marketplaceWebTablet) { + marketplaceWebTablet.getOverlayObject().emitScriptEvent(message); + } +} + +function onClick() { + toggleMarketplace(); +} + +browseExamplesButton.clicked.connect(onClick); +marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged); +Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); + +clearOldTablet(); // Run once at startup, in case there's anything laying around from a crash. +// We could also optionally do something like Window.domainChanged.connect(function () {Script.setTimeout(clearOldTablet, 2000)}), +// but the HUD version stays around, so lets do the same. + +Script.scriptEnding.connect(function () { + toolBar.removeButton("marketplace"); + browseExamplesButton.clicked.disconnect(onClick); + marketplaceWindow.visibleChanged.disconnect(onMarketplaceWindowVisibilityChanged); + Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); +}); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/muteHUD.js b/scripts/system/muteHUD.js new file mode 100644 index 0000000000..722ed65b3d --- /dev/null +++ b/scripts/system/muteHUD.js @@ -0,0 +1,51 @@ +"use strict"; + +// +// goto.js +// scripts/system/ +// +// Created by Howard Stearns on 2 Jun 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + +var button = toolBar.addButton({ + objectName: "mute", + imageURL: Script.resolvePath("assets/images/tools/mic.svg"), + visible: true, + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 +}); + +function onMuteToggled() { + // We could just toggle state, but we're less likely to get out of wack if we read the AudioDevice. + // muted => button "on" state => 1. go figure. + var state = AudioDevice.getMuted() ? 0 : 1; + var hoverState = AudioDevice.getMuted() ? 2 : 3; + button.writeProperty('buttonState', state); + button.writeProperty('defaultState', state); + button.writeProperty('hoverState', hoverState); +} +onMuteToggled(); +function onClicked(){ + var menuItem = "Mute Microphone"; + Menu.setIsOptionChecked(menuItem, !Menu.isOptionChecked(menuItem)); +} +button.clicked.connect(onClicked); +AudioDevice.muteToggled.connect(onMuteToggled); + +Script.scriptEnding.connect(function () { + toolBar.removeButton("mute"); + button.clicked.disconnect(onClicked); + AudioDevice.muteToggled.disconnect(onMuteToggled); +}); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/palHUD.js b/scripts/system/palHUD.js new file mode 100644 index 0000000000..f148ad5fdb --- /dev/null +++ b/scripts/system/palHUD.js @@ -0,0 +1,658 @@ +"use strict"; +/*jslint vars: true, plusplus: true, forin: true*/ +/*globals Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, OverlayWindow, Toolbars, Vec3, Quat, Controller, print, getControllerWorldLocation */ +// +// pal.js +// +// Created by Howard Stearns on December 9, 2016 +// Copyright 2016 High Fidelity, Inc +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed +// something, will revisit as this is sorta horrible. +const UNSELECTED_TEXTURES = {"idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png"), + "idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png") +}; +const SELECTED_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png"), + "idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png") +}; +const HOVER_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-hover.png"), + "idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-hover.png") +}; + +const UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6}; +const SELECTED_COLOR = {red: 0xF3, green: 0x91, blue: 0x29}; +const HOVER_COLOR = {red: 0xD0, green: 0xD0, blue: 0xD0}; // almost white for now + +(function() { // BEGIN LOCAL_SCOPE + +Script.include("/~/system/libraries/controllers.js"); + +// +// Overlays. +// +var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier. + +function ExtendedOverlay(key, type, properties, selected, hasModel) { // A wrapper around overlays to store the key it is associated with. + overlays[key] = this; + if (hasModel) { + var modelKey = key + "-m"; + this.model = new ExtendedOverlay(modelKey, "model", { + url: Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx"), + textures: textures(selected), + ignoreRayIntersection: true + }, false, false); + } else { + this.model = undefined; + } + this.key = key; + this.selected = selected || false; // not undefined + this.hovering = false; + this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected... +} +// Instance methods: +ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay + Overlays.deleteOverlay(this.activeOverlay); + delete overlays[this.key]; +}; + +ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay + Overlays.editOverlay(this.activeOverlay, properties); +}; + +function color(selected, hovering, level) { + var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; + function scale(component) { + var delta = 0xFF - component; + return component + (delta * level); + } + return {red: scale(base.red), green: scale(base.green), blue: scale(base.blue)}; +} + +function textures(selected, hovering) { + return hovering ? HOVER_TEXTURES : selected ? SELECTED_TEXTURES : UNSELECTED_TEXTURES; +} +// so we don't have to traverse the overlays to get the last one +var lastHoveringId = 0; +ExtendedOverlay.prototype.hover = function (hovering) { + this.hovering = hovering; + if (this.key === lastHoveringId) { + if (hovering) { + return; + } else { + lastHoveringId = 0; + } + } + this.editOverlay({color: color(this.selected, hovering, this.audioLevel)}); + if (this.model) { + this.model.editOverlay({textures: textures(this.selected, hovering)}); + } + if (hovering) { + // un-hover the last hovering overlay + if (lastHoveringId && lastHoveringId != this.key) { + ExtendedOverlay.get(lastHoveringId).hover(false); + } + lastHoveringId = this.key; + } +} +ExtendedOverlay.prototype.select = function (selected) { + if (this.selected === selected) { + return; + } + + UserActivityLogger.palAction(selected ? "avatar_selected" : "avatar_deselected", this.key); + + this.editOverlay({color: color(selected, this.hovering, this.audioLevel)}); + if (this.model) { + this.model.editOverlay({textures: textures(selected)}); + } + this.selected = selected; +}; +// Class methods: +var selectedIds = []; +ExtendedOverlay.isSelected = function (id) { + return -1 !== selectedIds.indexOf(id); +}; +ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier + return overlays[key]; +}; +ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy. + var key; + for (key in overlays) { + if (iterator(ExtendedOverlay.get(key))) { + return; + } + } +}; +ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any) + if (lastHoveringId) { + ExtendedOverlay.get(lastHoveringId).hover(false); + } +}; + +// hit(overlay) on the one overlay intersected by pickRay, if any. +// noHit() if no ExtendedOverlay was intersected (helps with hover) +ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { + var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. + if (!pickedOverlay.intersects) { + if (noHit) { + return noHit(); + } + return; + } + ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. + if ((overlay.activeOverlay) === pickedOverlay.overlayID) { + hit(overlay); + return true; + } + }); +}; + + +// +// Similar, for entities +// +function HighlightedEntity(id, entityProperties) { + this.id = id; + this.overlay = Overlays.addOverlay('cube', { + position: entityProperties.position, + rotation: entityProperties.rotation, + dimensions: entityProperties.dimensions, + solid: false, + color: { + red: 0xF3, + green: 0x91, + blue: 0x29 + }, + lineWidth: 1.0, + ignoreRayIntersection: true, + drawInFront: false // Arguable. For now, let's not distract with mysterious wires around the scene. + }); + HighlightedEntity.overlays.push(this); +} +HighlightedEntity.overlays = []; +HighlightedEntity.clearOverlays = function clearHighlightedEntities() { + HighlightedEntity.overlays.forEach(function (highlighted) { + Overlays.deleteOverlay(highlighted.overlay); + }); + HighlightedEntity.overlays = []; +}; +HighlightedEntity.updateOverlays = function updateHighlightedEntities() { + HighlightedEntity.overlays.forEach(function (highlighted) { + var properties = Entities.getEntityProperties(highlighted.id, ['position', 'rotation', 'dimensions']); + Overlays.editOverlay(highlighted.overlay, { + position: properties.position, + rotation: properties.rotation, + dimensions: properties.dimensions + }); + }); +}; + +// +// The qml window and communications. +// +var pal = new OverlayWindow({ + title: 'People Action List', + source: 'hifi/Pal.qml', + width: 580, + height: 640, + visible: false +}); +pal.fromQml.connect(function (message) { // messages are {method, params}, like json-rpc. See also sendToQml. + print('From PAL QML:', JSON.stringify(message)); + switch (message.method) { + case 'selected': + selectedIds = message.params; + ExtendedOverlay.some(function (overlay) { + var id = overlay.key; + var selected = ExtendedOverlay.isSelected(id); + overlay.select(selected); + }); + + HighlightedEntity.clearOverlays(); + if (selectedIds.length) { + Entities.findEntitiesInFrustum(Camera.frustum).forEach(function (id) { + // Because lastEditedBy is per session, the vast majority of entities won't match, + // so it would probably be worth reducing marshalling costs by asking for just we need. + // However, providing property name(s) is advisory and some additional properties are + // included anyway. As it turns out, asking for 'lastEditedBy' gives 'position', 'rotation', + // and 'dimensions', too, so we might as well make use of them instead of making a second + // getEntityProperties call. + // It would be nice if we could harden this against future changes by specifying all + // and only these four in an array, but see + // https://highfidelity.fogbugz.com/f/cases/2728/Entities-getEntityProperties-id-lastEditedBy-name-lastEditedBy-doesn-t-work + var properties = Entities.getEntityProperties(id, 'lastEditedBy'); + if (ExtendedOverlay.isSelected(properties.lastEditedBy)) { + new HighlightedEntity(id, properties); + } + }); + } + break; + case 'refresh': + removeOverlays(); + populateUserList(message.params); + UserActivityLogger.palAction("refresh", ""); + break; + case 'updateGain': + data = message.params; + if (data['isReleased']) { + // isReleased=true happens once at the end of a cycle of dragging + // the slider about, but with same gain as last isReleased=false so + // we don't set the gain in that case, and only here do we want to + // send an analytic event. + UserActivityLogger.palAction("avatar_gain_changed", data['sessionId']); + } else { + Users.setAvatarGain(data['sessionId'], data['gain']); + } + break; + case 'displayNameUpdate': + if (MyAvatar.displayName != message.params) { + MyAvatar.displayName = message.params; + UserActivityLogger.palAction("display_name_change", ""); + } + break; + default: + print('Unrecognized message from Pal.qml:', JSON.stringify(message)); + } +}); + +// +// Main operations. +// +function addAvatarNode(id) { + var selected = ExtendedOverlay.isSelected(id); + return new ExtendedOverlay(id, "sphere", { + drawInFront: true, + solid: true, + alpha: 0.8, + color: color(selected, false, 0.0), + ignoreRayIntersection: false}, selected, true); +} +function populateUserList(selectData) { + var data = []; + AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { // sorting the identifiers is just an aid for debugging + var avatar = AvatarList.getAvatar(id); + var avatarPalDatum = { + displayName: avatar.sessionDisplayName, + userName: '', + sessionId: id || '', + audioLevel: 0.0, + admin: false + }; + // Request the username, fingerprint, and admin status from the given UUID + // Username and fingerprint returns default constructor output if the requesting user isn't an admin + Users.requestUsernameFromID(id); + // Request personal mute status and ignore status + // from NodeList (as long as we're not requesting it for our own ID) + if (id) { + avatarPalDatum['personalMute'] = Users.getPersonalMuteStatus(id); + avatarPalDatum['ignore'] = Users.getIgnoreStatus(id); + addAvatarNode(id); // No overlay for ourselves + } + data.push(avatarPalDatum); + print('PAL data:', JSON.stringify(avatarPalDatum)); + }); + pal.sendToQml({ method: 'users', params: data }); + if (selectData) { + selectData[2] = true; + pal.sendToQml({ method: 'select', params: selectData }); + } +} + +// The function that handles the reply from the server +function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { + var data; + // If the ID we've received is our ID... + if (MyAvatar.sessionUUID === id) { + // Set the data to contain specific strings. + data = ['', username, isAdmin]; + } else if (Users.canKick) { + // Set the data to contain the ID and the username (if we have one) + // or fingerprint (if we don't have a username) string. + data = [id, username || machineFingerprint, isAdmin]; + } else { + // Set the data to contain specific strings. + data = [id, '', isAdmin]; + } + print('Username Data:', JSON.stringify(data)); + // Ship the data off to QML + pal.sendToQml({ method: 'updateUsername', params: data }); +} + +var pingPong = true; +function updateOverlays() { + var eye = Camera.position; + AvatarList.getAvatarIdentifiers().forEach(function (id) { + if (!id) { + return; // don't update ourself + } + + var overlay = ExtendedOverlay.get(id); + if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. + print('Adding non-PAL avatar node', id); + overlay = addAvatarNode(id); + } + var avatar = AvatarList.getAvatar(id); + var target = avatar.position; + var distance = Vec3.distance(target, eye); + var offset = 0.2; + + // base offset on 1/2 distance from hips to head if we can + var headIndex = avatar.getJointIndex("Head"); + if (headIndex > 0) { + offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; + } + + // get diff between target and eye (a vector pointing to the eye from avatar position) + var diff = Vec3.subtract(target, eye); + + // move a bit in front, towards the camera + target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); + + // now bump it up a bit + target.y = target.y + offset; + + overlay.ping = pingPong; + overlay.editOverlay({ + color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel), + position: target, + dimensions: 0.032 * distance + }); + if (overlay.model) { + overlay.model.ping = pingPong; + overlay.model.editOverlay({ + position: target, + scale: 0.2 * distance, // constant apparent size + rotation: Camera.orientation + }); + } + }); + pingPong = !pingPong; + ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.) + if (overlay.ping === pingPong) { + overlay.deleteOverlay(); + } + }); + // We could re-populateUserList if anything added or removed, but not for now. + HighlightedEntity.updateOverlays(); +} +function removeOverlays() { + selectedIds = []; + lastHoveringId = 0; + HighlightedEntity.clearOverlays(); + ExtendedOverlay.some(function (overlay) { overlay.deleteOverlay(); }); +} + +// +// Clicks. +// +function handleClick(pickRay) { + ExtendedOverlay.applyPickRay(pickRay, function (overlay) { + // Don't select directly. Tell qml, who will give us back a list of ids. + var message = {method: 'select', params: [[overlay.key], !overlay.selected, false]}; + pal.sendToQml(message); + return true; + }); +} +function handleMouseEvent(mousePressEvent) { // handleClick if we get one. + if (!mousePressEvent.isLeftButton) { + return; + } + handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); +} +function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic + ExtendedOverlay.applyPickRay(pickRay, function (overlay) { + overlay.hover(true); + }, function () { + ExtendedOverlay.unHover(); + }); +} + +// handy global to keep track of which hand is the mouse (if any) +var currentHandPressed = 0; +const TRIGGER_CLICK_THRESHOLD = 0.85; +const TRIGGER_PRESS_THRESHOLD = 0.05; + +function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position + if (HMD.active) { + if (currentHandPressed != 0) { + pickRay = controllerComputePickRay(currentHandPressed); + } else { + // nothing should hover, so + ExtendedOverlay.unHover(); + return; + } + } else { + pickRay = Camera.computePickRay(event.x, event.y); + } + handleMouseMove(pickRay); +} +function handleTriggerPressed(hand, value) { + // The idea is if you press one trigger, it is the one + // we will consider the mouse. Even if the other is pressed, + // we ignore it until this one is no longer pressed. + isPressed = value > TRIGGER_PRESS_THRESHOLD; + if (currentHandPressed == 0) { + currentHandPressed = isPressed ? hand : 0; + return; + } + if (currentHandPressed == hand) { + currentHandPressed = isPressed ? hand : 0; + return; + } + // otherwise, the other hand is still triggered + // so do nothing. +} + +// We get mouseMoveEvents from the handControllers, via handControllerPointer. +// But we don't get mousePressEvents. +var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); +var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); +function controllerComputePickRay(hand) { + var controllerPose = getControllerWorldLocation(hand, true); + if (controllerPose.valid) { + return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) }; + } +} +function makeClickHandler(hand) { + return function (clicked) { + if (clicked > TRIGGER_CLICK_THRESHOLD) { + var pickRay = controllerComputePickRay(hand); + handleClick(pickRay); + } + }; +} +function makePressHandler(hand) { + return function (value) { + handleTriggerPressed(hand, value); + } +} +triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); +triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); +triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); +triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); +// +// Message from other scripts, such as edit.js +// +var CHANNEL = 'com.highfidelity.pal'; +function receiveMessage(channel, messageString, senderID) { + if ((channel !== CHANNEL) || + (senderID !== MyAvatar.sessionUUID)) { + return; + } + var message = JSON.parse(messageString); + switch (message.method) { + case 'select': + if (!pal.visible) { + onClicked(); + } + pal.sendToQml(message); // Accepts objects, not just strings. + break; + default: + print('Unrecognized PAL message', messageString); + } +} +Messages.subscribe(CHANNEL); +Messages.messageReceived.connect(receiveMessage); + + +var AVERAGING_RATIO = 0.05; +var LOUDNESS_FLOOR = 11.0; +var LOUDNESS_SCALE = 2.8 / 5.0; +var LOG2 = Math.log(2.0); +var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) +var myData = {}; // we're not includied in ExtendedOverlay.get. +var audioInterval; + +function getAudioLevel(id) { + // the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged + // But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency + // of updating (the latter for efficiency too). + var avatar = AvatarList.getAvatar(id); + var audioLevel = 0.0; + var data = id ? ExtendedOverlay.get(id) : myData; + if (!data) { + return audioLevel; + } + + // we will do exponential moving average by taking some the last loudness and averaging + data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatar.audioLoudness); + + // add 1 to insure we don't go log() and hit -infinity. Math.log is + // natural log, so to get log base 2, just divide by ln(2). + var logLevel = Math.log(data.accumulatedLevel + 1) / LOG2; + + if (logLevel <= LOUDNESS_FLOOR) { + audioLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE; + } else { + audioLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE; + } + if (audioLevel > 1.0) { + audioLevel = 1; + } + data.audioLevel = audioLevel; + return audioLevel; +} + +function createAudioInterval() { + // we will update the audioLevels periodically + // TODO: tune for efficiency - expecially with large numbers of avatars + return Script.setInterval(function () { + if (pal.visible) { + var param = {}; + AvatarList.getAvatarIdentifiers().forEach(function (id) { + var level = getAudioLevel(id); + // qml didn't like an object with null/empty string for a key, so... + var userId = id || 0; + param[userId] = level; + }); + pal.sendToQml({method: 'updateAudioLevel', params: param}); + } + }, AUDIO_LEVEL_UPDATE_INTERVAL_MS); +} + +// +// Manage the connection between the button and the window. +// +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +var buttonName = "pal"; +var button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/people.svg"), + visible: true, + hoverState: 2, + defaultState: 1, + buttonState: 1, + alpha: 0.9 +}); + +var isWired = false; +var palOpenedAt; + +function off() { + if (isWired) { // It is not ok to disconnect these twice, hence guard. + Script.update.disconnect(updateOverlays); + Controller.mousePressEvent.disconnect(handleMouseEvent); + Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); + isWired = false; + } + triggerMapping.disable(); // It's ok if we disable twice. + triggerPressMapping.disable(); // see above + removeOverlays(); + Users.requestsDomainListData = false; + if (palOpenedAt) { + var duration = new Date().getTime() - palOpenedAt; + UserActivityLogger.palOpened(duration / 1000.0); + palOpenedAt = 0; // just a falsy number is good enough. + } + if (audioInterval) { + Script.clearInterval(audioInterval); + } +} +function onClicked() { + if (!pal.visible) { + Users.requestsDomainListData = true; + populateUserList(); + pal.raise(); + isWired = true; + Script.update.connect(updateOverlays); + Controller.mousePressEvent.connect(handleMouseEvent); + Controller.mouseMoveEvent.connect(handleMouseMoveEvent); + triggerMapping.enable(); + triggerPressMapping.enable(); + createAudioInterval(); + palOpenedAt = new Date().getTime(); + } else { + off(); + } + pal.setVisible(!pal.visible); +} +function avatarDisconnected(nodeID) { + // remove from the pal list + pal.sendToQml({method: 'avatarDisconnected', params: [nodeID]}); +} +// +// Button state. +// +function onVisibleChanged() { + button.writeProperty('buttonState', pal.visible ? 0 : 1); + button.writeProperty('defaultState', pal.visible ? 0 : 1); + button.writeProperty('hoverState', pal.visible ? 2 : 3); +} +button.clicked.connect(onClicked); +pal.visibleChanged.connect(onVisibleChanged); +pal.closed.connect(off); +Users.usernameFromIDReply.connect(usernameFromIDReply); +Users.avatarDisconnected.connect(avatarDisconnected); + +function clearLocalQMLDataAndClosePAL() { + pal.sendToQml({ method: 'clearLocalQMLData' }); + if (pal.visible) { + onClicked(); // Close the PAL + } +} +Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); +Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); + +// +// Cleanup. +// +Script.scriptEnding.connect(function () { + button.clicked.disconnect(onClicked); + toolBar.removeButton(buttonName); + pal.visibleChanged.disconnect(onVisibleChanged); + pal.closed.disconnect(off); + Users.usernameFromIDReply.disconnect(usernameFromIDReply); + Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL); + Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL); + Messages.unsubscribe(CHANNEL); + Messages.messageReceived.disconnect(receiveMessage); + Users.avatarDisconnected.disconnect(avatarDisconnected); + off(); +}); + + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/snapshotHUD.js b/scripts/system/snapshotHUD.js new file mode 100644 index 0000000000..d79a6e46cb --- /dev/null +++ b/scripts/system/snapshotHUD.js @@ -0,0 +1,226 @@ +// +// snapshot.js +// +// Created by David Kelly on 1 August 2016 +// Copyright 2016 High Fidelity, Inc +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + +var SNAPSHOT_DELAY = 500; // 500ms +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +var resetOverlays; +var reticleVisible; +var clearOverlayWhenMoving; +var button = toolBar.addButton({ + objectName: "snapshot", + imageURL: Script.resolvePath("assets/images/tools/snap.svg"), + visible: true, + buttonState: 1, + defaultState: 1, + hoverState: 2, + alpha: 0.9, +}); + +function shouldOpenFeedAfterShare() { + var persisted = Settings.getValue('openFeedAfterShare', true); // might answer true, false, "true", or "false" + return persisted && (persisted !== 'false'); +} +function showFeedWindow() { + DialogsManager.showFeed(); +} + +var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html"); + +var outstanding; +function confirmShare(data) { + var dialog = new OverlayWebWindow('Snapshot Review', SNAPSHOT_REVIEW_URL, 800, 520); + function onMessage(message) { + // Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following: + // 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.) + // 2. Although we currently use a single image, we would like to take snapshot, a selfie, a 360 etc. all at the + // same time, show the user all of them, and have the user deselect any that they do not want to share. + // So we'll ultimately be receiving a set of objects, perhaps with different post processing for each. + var isLoggedIn; + var needsLogin = false; + switch (message) { + case 'ready': + dialog.emitScriptEvent(data); // Send it. + outstanding = 0; + break; + case 'openSettings': + Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog"); + break; + case 'setOpenFeedFalse': + Settings.setValue('openFeedAfterShare', false) + break; + case 'setOpenFeedTrue': + Settings.setValue('openFeedAfterShare', true) + break; + default: + dialog.webEventReceived.disconnect(onMessage); + dialog.close(); + isLoggedIn = Account.isLoggedIn(); + message.forEach(function (submessage) { + if (submessage.share && !isLoggedIn) { + needsLogin = true; + submessage.share = false; + } + if (submessage.share) { + print('sharing', submessage.localPath); + outstanding++; + Window.shareSnapshot(submessage.localPath, submessage.href); + } else { + print('not sharing', submessage.localPath); + } + }); + if (!outstanding && shouldOpenFeedAfterShare()) { + showFeedWindow(); + } + if (needsLogin) { // after the possible feed, so that the login is on top + Account.checkAndSignalForAccessToken(); + } + } + } + dialog.webEventReceived.connect(onMessage); + dialog.raise(); +} + +function snapshotShared(errorMessage) { + if (!errorMessage) { + print('snapshot uploaded and shared'); + } else { + print(errorMessage); + } + if ((--outstanding <= 0) && shouldOpenFeedAfterShare()) { + showFeedWindow(); + } +} +var href, domainId; +function onClicked() { + // Raising the desktop for the share dialog at end will interact badly with clearOverlayWhenMoving. + // Turn it off now, before we start futzing with things (and possibly moving). + clearOverlayWhenMoving = MyAvatar.getClearOverlayWhenMoving(); // Do not use Settings. MyAvatar keeps a separate copy. + MyAvatar.setClearOverlayWhenMoving(false); + + // We will record snapshots based on the starting location. That could change, e.g., when recording a .gif. + // Even the domainId could change (e.g., if the user falls into a teleporter while recording). + href = location.href; + domainId = location.domainId; + + // update button states + resetOverlays = Menu.isOptionChecked("Overlays"); // For completness. Certainly true if the button is visible to be clicke. + reticleVisible = Reticle.visible; + Reticle.visible = false; + Window.snapshotTaken.connect(resetButtons); + + button.writeProperty("buttonState", 0); + button.writeProperty("defaultState", 0); + button.writeProperty("hoverState", 2); + + // hide overlays if they are on + if (resetOverlays) { + Menu.setIsOptionChecked("Overlays", false); + } + + // hide hud + toolBar.writeProperty("visible", false); + + // take snapshot (with no notification) + Script.setTimeout(function () { + Window.takeSnapshot(false, true, 1.91); + }, SNAPSHOT_DELAY); +} + +function isDomainOpen(id) { + var request = new XMLHttpRequest(); + var options = [ + 'now=' + new Date().toISOString(), + 'include_actions=concurrency', + 'domain_id=' + id.slice(1, -1), + 'restriction=open,hifi' // If we're sharing, we're logged in + // If we're here, protocol matches, and it is online + ]; + var url = location.metaverseServerUrl + "/api/v1/user_stories?" + options.join('&'); + request.open("GET", url, false); + request.send(); + if (request.status != 200) { + return false; + } + var response = JSON.parse(request.response); // Not parsed for us. + return (response.status === 'success') && + response.total_entries; +} + +function resetButtons(pathStillSnapshot, pathAnimatedSnapshot, notify) { + // If we're not taking an animated snapshot, we have to show the HUD. + // If we ARE taking an animated snapshot, we've already re-enabled the HUD by this point. + if (pathAnimatedSnapshot === "") { + // show hud + toolBar.writeProperty("visible", true); + Reticle.visible = reticleVisible; + // show overlays if they were on + if (resetOverlays) { + Menu.setIsOptionChecked("Overlays", true); + } + } else { + // Allow the user to click the snapshot HUD button again + button.clicked.connect(onClicked); + } + // update button states + button.writeProperty("buttonState", 1); + button.writeProperty("defaultState", 1); + button.writeProperty("hoverState", 3); + Window.snapshotTaken.disconnect(resetButtons); + + // A Snapshot Review dialog might be left open indefinitely after taking the picture, + // during which time the user may have moved. So stash that info in the dialog so that + // it records the correct href. (We can also stash in .jpegs, but not .gifs.) + // last element in data array tells dialog whether we can share or not + var confirmShareContents = [ + { localPath: pathStillSnapshot, href: href }, + { + canShare: !!isDomainOpen(domainId), + openFeedAfterShare: shouldOpenFeedAfterShare() + }]; + if (pathAnimatedSnapshot !== "") { + confirmShareContents.unshift({ localPath: pathAnimatedSnapshot, href: href }); + } + confirmShare(confirmShareContents); + if (clearOverlayWhenMoving) { + MyAvatar.setClearOverlayWhenMoving(true); // not until after the share dialog + } +} + +function processingGif() { + // show hud + toolBar.writeProperty("visible", true); + Reticle.visible = reticleVisible; + + // update button states + button.writeProperty("buttonState", 0); + button.writeProperty("defaultState", 0); + button.writeProperty("hoverState", 2); + // Don't allow the user to click the snapshot button yet + button.clicked.disconnect(onClicked); + // show overlays if they were on + if (resetOverlays) { + Menu.setIsOptionChecked("Overlays", true); + } +} + +button.clicked.connect(onClicked); +Window.snapshotShared.connect(snapshotShared); +Window.processingGif.connect(processingGif); + +Script.scriptEnding.connect(function () { + toolBar.removeButton("snapshot"); + button.clicked.disconnect(onClicked); + Window.snapshotShared.disconnect(snapshotShared); + Window.processingGif.disconnect(processingGif); +}); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/usersHUD.js b/scripts/system/usersHUD.js new file mode 100644 index 0000000000..8c52240aa9 --- /dev/null +++ b/scripts/system/usersHUD.js @@ -0,0 +1,1237 @@ +"use strict"; + +// +// users.js +// examples +// +// Created by David Rowe on 9 Mar 2015. +// 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 +// + +(function() { // BEGIN LOCAL_SCOPE + +// resolve these paths immediately +var MIN_MAX_BUTTON_SVG = Script.resolvePath("assets/images/tools/min-max-toggle.svg"); +var BASE_URL = Script.resolvePath("assets/images/tools/"); + +var PopUpMenu = function (properties) { + var value = properties.value, + promptOverlay, + valueOverlay, + buttonOverlay, + optionOverlays = [], + isDisplayingOptions = false, + OPTION_MARGIN = 4, + + MIN_MAX_BUTTON_SVG_WIDTH = 17.1, + MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, + MIN_MAX_BUTTON_WIDTH = 14, + MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH; + + function positionDisplayOptions() { + var y, + i; + + y = properties.y - (properties.values.length - 1) * properties.lineHeight - OPTION_MARGIN; + + for (i = 0; i < properties.values.length; i += 1) { + Overlays.editOverlay(optionOverlays[i], { + y: y + }); + y += properties.lineHeight; + } + } + + function showDisplayOptions() { + var i, + yOffScreen = Controller.getViewportDimensions().y; + + for (i = 0; i < properties.values.length; i += 1) { + optionOverlays[i] = Overlays.addOverlay("text", { + x: properties.x + properties.promptWidth, + y: yOffScreen, + width: properties.width - properties.promptWidth, + height: properties.textHeight + OPTION_MARGIN, // Only need to add margin at top to balance descenders + topMargin: OPTION_MARGIN, + leftMargin: OPTION_MARGIN, + color: properties.optionColor, + alpha: properties.optionAlpha, + backgroundColor: properties.popupBackgroundColor, + backgroundAlpha: properties.popupBackgroundAlpha, + text: properties.displayValues[i], + font: properties.font, + visible: true + }); + } + + positionDisplayOptions(); + + isDisplayingOptions = true; + } + + function deleteDisplayOptions() { + var i; + + for (i = 0; i < optionOverlays.length; i += 1) { + Overlays.deleteOverlay(optionOverlays[i]); + } + + isDisplayingOptions = false; + } + + function handleClick(overlay) { + var clicked = false, + i; + + if (overlay === valueOverlay || overlay === buttonOverlay) { + showDisplayOptions(); + return true; + } + + if (isDisplayingOptions) { + for (i = 0; i < optionOverlays.length; i += 1) { + if (overlay === optionOverlays[i]) { + value = properties.values[i]; + Overlays.editOverlay(valueOverlay, { + text: properties.displayValues[i] + }); + clicked = true; + } + } + + deleteDisplayOptions(); + } + + return clicked; + } + + function updatePosition(x, y) { + properties.x = x; + properties.y = y; + Overlays.editOverlay(promptOverlay, { + x: x, + y: y + }); + Overlays.editOverlay(valueOverlay, { + x: x + properties.promptWidth, + y: y - OPTION_MARGIN + }); + Overlays.editOverlay(buttonOverlay, { + x: x + properties.width - MIN_MAX_BUTTON_WIDTH - 1, + y: y - OPTION_MARGIN + 1 + }); + if (isDisplayingOptions) { + positionDisplayOptions(); + } + } + + function setVisible(visible) { + Overlays.editOverlay(promptOverlay, { + visible: visible + }); + Overlays.editOverlay(valueOverlay, { + visible: visible + }); + Overlays.editOverlay(buttonOverlay, { + visible: visible + }); + } + + function tearDown() { + Overlays.deleteOverlay(promptOverlay); + Overlays.deleteOverlay(valueOverlay); + Overlays.deleteOverlay(buttonOverlay); + if (isDisplayingOptions) { + deleteDisplayOptions(); + } + } + + function getValue() { + return value; + } + + function setValue(newValue) { + var index; + + index = properties.values.indexOf(newValue); + if (index !== -1) { + value = newValue; + Overlays.editOverlay(valueOverlay, { + text: properties.displayValues[index] + }); + } + } + + promptOverlay = Overlays.addOverlay("text", { + x: properties.x, + y: properties.y, + width: properties.promptWidth, + height: properties.textHeight, + topMargin: 0, + leftMargin: 0, + color: properties.promptColor, + alpha: properties.promptAlpha, + backgroundColor: properties.promptBackgroundColor, + backgroundAlpha: properties.promptBackgroundAlpha, + text: properties.prompt, + font: properties.font, + visible: properties.visible + }); + + valueOverlay = Overlays.addOverlay("text", { + x: properties.x + properties.promptWidth, + y: properties.y, + width: properties.width - properties.promptWidth, + height: properties.textHeight + OPTION_MARGIN, // Only need to add margin at top to balance descenders + topMargin: OPTION_MARGIN, + leftMargin: OPTION_MARGIN, + color: properties.optionColor, + alpha: properties.optionAlpha, + backgroundColor: properties.optionBackgroundColor, + backgroundAlpha: properties.optionBackgroundAlpha, + text: properties.displayValues[properties.values.indexOf(value)], + font: properties.font, + visible: properties.visible + }); + + buttonOverlay = Overlays.addOverlay("image", { + x: properties.x + properties.width - MIN_MAX_BUTTON_WIDTH - 1, + y: properties.y, + width: MIN_MAX_BUTTON_WIDTH, + height: MIN_MAX_BUTTON_HEIGHT, + imageURL: MIN_MAX_BUTTON_SVG, + subImage: { + x: 0, + y: 0, + width: MIN_MAX_BUTTON_SVG_WIDTH, + height: MIN_MAX_BUTTON_SVG_HEIGHT / 2 + }, + //color: properties.buttonColor, + alpha: properties.buttonAlpha, + visible: properties.visible + }); + + return { + updatePosition: updatePosition, + setVisible: setVisible, + handleClick: handleClick, + tearDown: tearDown, + getValue: getValue, + setValue: setValue + }; +}; + +var usersWindow = (function () { + + var WINDOW_WIDTH = 260, + WINDOW_MARGIN = 12, + WINDOW_BASE_MARGIN = 24, // A little less is needed in order look correct + WINDOW_FONT = { + size: 12 + }, + WINDOW_FOREGROUND_COLOR = { + red: 240, + green: 240, + blue: 240 + }, + WINDOW_FOREGROUND_ALPHA = 0.95, + WINDOW_HEADING_COLOR = { + red: 180, + green: 180, + blue: 180 + }, + WINDOW_HEADING_ALPHA = 0.95, + WINDOW_BACKGROUND_COLOR = { + red: 80, + green: 80, + blue: 80 + }, + WINDOW_BACKGROUND_ALPHA = 0.8, + windowPane, + windowHeading, + + // Margin on the left and right side of the window to keep + // it from getting too close to the edge of the screen which + // is unclickable. + WINDOW_MARGIN_X = 20, + + // Window border is similar to that of edit.js. + WINDOW_MARGIN_HALF = WINDOW_MARGIN / 2, + WINDOW_BORDER_WIDTH = WINDOW_WIDTH + 2 * WINDOW_MARGIN_HALF, + WINDOW_BORDER_TOP_MARGIN = 2 * WINDOW_MARGIN_HALF, + WINDOW_BORDER_BOTTOM_MARGIN = WINDOW_MARGIN_HALF, + WINDOW_BORDER_LEFT_MARGIN = WINDOW_MARGIN_HALF, + WINDOW_BORDER_RADIUS = 4, + WINDOW_BORDER_COLOR = { red: 255, green: 255, blue: 255 }, + WINDOW_BORDER_ALPHA = 0.5, + windowBorder, + + MIN_MAX_BUTTON_SVG = BASE_URL + "min-max-toggle.svg", + MIN_MAX_BUTTON_SVG_WIDTH = 17.1, + MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, + MIN_MAX_BUTTON_WIDTH = 14, + MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH, + MIN_MAX_BUTTON_COLOR = { + red: 255, + green: 255, + blue: 255 + }, + MIN_MAX_BUTTON_ALPHA = 0.9, + minimizeButton, + SCROLLBAR_BACKGROUND_WIDTH = 12, + SCROLLBAR_BACKGROUND_COLOR = { + red: 70, + green: 70, + blue: 70 + }, + SCROLLBAR_BACKGROUND_ALPHA = 0.8, + scrollbarBackground, + SCROLLBAR_BAR_MIN_HEIGHT = 5, + SCROLLBAR_BAR_COLOR = { + red: 170, + green: 170, + blue: 170 + }, + SCROLLBAR_BAR_ALPHA = 0.8, + SCROLLBAR_BAR_SELECTED_ALPHA = 0.95, + scrollbarBar, + scrollbarBackgroundHeight, + scrollbarBarHeight, + FRIENDS_BUTTON_SPACER = 6, // Space before add/remove friends button + FRIENDS_BUTTON_SVG = BASE_URL + "add-remove-friends.svg", + FRIENDS_BUTTON_SVG_WIDTH = 107, + FRIENDS_BUTTON_SVG_HEIGHT = 27, + FRIENDS_BUTTON_WIDTH = FRIENDS_BUTTON_SVG_WIDTH, + FRIENDS_BUTTON_HEIGHT = FRIENDS_BUTTON_SVG_HEIGHT, + FRIENDS_BUTTON_COLOR = { + red: 225, + green: 225, + blue: 225 + }, + FRIENDS_BUTTON_ALPHA = 0.95, + FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends", + FRIENDS_WINDOW_WIDTH = 290, + FRIENDS_WINDOW_HEIGHT = 500, + FRIENDS_WINDOW_TITLE = "Add/Remove Friends", + friendsButton, + friendsWindow, + + OPTION_BACKGROUND_COLOR = { + red: 60, + green: 60, + blue: 60 + }, + OPTION_BACKGROUND_ALPHA = 0.1, + + DISPLAY_SPACER = 12, // Space before display control + DISPLAY_PROMPT = "Show me:", + DISPLAY_PROMPT_WIDTH = 60, + DISPLAY_EVERYONE = "everyone", + DISPLAY_FRIENDS = "friends", + DISPLAY_VALUES = [DISPLAY_EVERYONE, DISPLAY_FRIENDS], + DISPLAY_DISPLAY_VALUES = DISPLAY_VALUES, + DISPLAY_OPTIONS_BACKGROUND_COLOR = { + red: 120, + green: 120, + blue: 120 + }, + DISPLAY_OPTIONS_BACKGROUND_ALPHA = 0.9, + displayControl, + + VISIBILITY_SPACER = 6, // Space before visibility control + VISIBILITY_PROMPT = "Visible to:", + VISIBILITY_PROMPT_WIDTH = 60, + VISIBILITY_ALL = "all", + VISIBILITY_FRIENDS = "friends", + VISIBILITY_NONE = "none", + VISIBILITY_VALUES = [VISIBILITY_ALL, VISIBILITY_FRIENDS, VISIBILITY_NONE], + VISIBILITY_DISPLAY_VALUES = ["everyone", "friends", "no one"], + visibilityControl, + + windowHeight, + windowBorderHeight, + windowTextHeight, + windowLineSpacing, + windowLineHeight, // = windowTextHeight + windowLineSpacing + windowMinimumHeight, + + usersOnline, // Raw users data + linesOfUsers = [], // Array of indexes pointing into usersOnline + numUsersToDisplay = 0, + firstUserToDisplay = 0, + + API_URL = "https://metaverse.highfidelity.com/api/v1/users?status=online", + API_FRIENDS_FILTER = "&filter=friends", + HTTP_GET_TIMEOUT = 60000, // ms = 1 minute + usersRequest, + processUsers, + pollUsersTimedOut, + usersTimer = null, + USERS_UPDATE_TIMEOUT = 5000, // ms = 5s + + showMe, + myVisibility, + + MENU_NAME = "View", + MENU_ITEM = "Users Online", + MENU_ITEM_OVERLAYS = "Overlays", + MENU_ITEM_AFTER = MENU_ITEM_OVERLAYS, + + SETTING_USERS_SHOW_ME = "UsersWindow.ShowMe", + SETTING_USERS_VISIBLE_TO = "UsersWindow.VisibleTo", + SETTING_USERS_WINDOW_MINIMIZED = "UsersWindow.Minimized", + SETTING_USERS_WINDOW_OFFSET = "UsersWindow.Offset", + // +ve x, y values are offset from left, top of screen; -ve from right, bottom. + + isLoggedIn = false, + isVisible = true, + isMinimized = false, + isBorderVisible = false, + + viewport, + isMirrorDisplay = false, + isFullscreenMirror = false, + + windowPosition = {}, // Bottom left corner of window pane. + isMovingWindow = false, + movingClickOffset = { x: 0, y: 0 }, + + isUsingScrollbars = false, + isMovingScrollbar = false, + scrollbarBackgroundPosition = {}, + scrollbarBarPosition = {}, + scrollbarBarClickedAt, // 0.0 .. 1.0 + scrollbarValue = 0.0; // 0.0 .. 1.0 + + function isWindowDisabled() { + return !Menu.isOptionChecked(MENU_ITEM) || !Menu.isOptionChecked(MENU_ITEM_OVERLAYS); + } + + function isValueTrue(value) { + // Work around Boolean Settings values being read as string when Interface starts up but as Booleans when re-read after + // Being written if refresh script. + return value === true || value === "true"; + } + + function calculateWindowHeight() { + var AUDIO_METER_HEIGHT = 52, + MIRROR_HEIGHT = 220, + nonUsersHeight, + maxWindowHeight; + + if (isMinimized) { + windowHeight = windowTextHeight + WINDOW_MARGIN + WINDOW_BASE_MARGIN; + windowBorderHeight = windowHeight + WINDOW_BORDER_TOP_MARGIN + WINDOW_BORDER_BOTTOM_MARGIN; + return; + } + + // Reserve space for title, friends button, and option controls + nonUsersHeight = WINDOW_MARGIN + windowLineHeight + + (shouldShowFriendsButton() ? FRIENDS_BUTTON_SPACER + FRIENDS_BUTTON_HEIGHT : 0) + + DISPLAY_SPACER + + windowLineHeight + VISIBILITY_SPACER + + windowLineHeight + WINDOW_BASE_MARGIN; + + // Limit window to height of viewport above window position minus VU meter and mirror if displayed + windowHeight = linesOfUsers.length * windowLineHeight - windowLineSpacing + nonUsersHeight; + maxWindowHeight = windowPosition.y - AUDIO_METER_HEIGHT; + if (isMirrorDisplay && !isFullscreenMirror) { + maxWindowHeight -= MIRROR_HEIGHT; + } + windowHeight = Math.max(Math.min(windowHeight, maxWindowHeight), nonUsersHeight); + windowBorderHeight = windowHeight + WINDOW_BORDER_TOP_MARGIN + WINDOW_BORDER_BOTTOM_MARGIN; + + // Corresponding number of users to actually display + numUsersToDisplay = Math.max(Math.round((windowHeight - nonUsersHeight) / windowLineHeight), 0); + isUsingScrollbars = 0 < numUsersToDisplay && numUsersToDisplay < linesOfUsers.length; + if (isUsingScrollbars) { + firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); + } else { + firstUserToDisplay = 0; + scrollbarValue = 0.0; + } + } + + function saturateWindowPosition() { + windowPosition.x = Math.max(WINDOW_MARGIN_X, Math.min(viewport.x - WINDOW_WIDTH - WINDOW_MARGIN_X, windowPosition.x)); + windowPosition.y = Math.max(windowMinimumHeight, Math.min(viewport.y, windowPosition.y)); + } + + function updateOverlayPositions() { + // Overlay positions are all relative to windowPosition; windowPosition is the position of the windowPane overlay. + var windowLeft = windowPosition.x, + windowTop = windowPosition.y - windowHeight, + x, + y; + + Overlays.editOverlay(windowBorder, { + x: windowPosition.x - WINDOW_BORDER_LEFT_MARGIN, + y: windowTop - WINDOW_BORDER_TOP_MARGIN + }); + Overlays.editOverlay(windowPane, { + x: windowLeft, + y: windowTop + }); + Overlays.editOverlay(windowHeading, { + x: windowLeft + WINDOW_MARGIN, + y: windowTop + WINDOW_MARGIN + }); + + Overlays.editOverlay(minimizeButton, { + x: windowLeft + WINDOW_WIDTH - WINDOW_MARGIN / 2 - MIN_MAX_BUTTON_WIDTH, + y: windowTop + WINDOW_MARGIN + }); + + scrollbarBackgroundPosition.x = windowLeft + WINDOW_WIDTH - 0.5 * WINDOW_MARGIN - SCROLLBAR_BACKGROUND_WIDTH; + scrollbarBackgroundPosition.y = windowTop + WINDOW_MARGIN + windowTextHeight; + Overlays.editOverlay(scrollbarBackground, { + x: scrollbarBackgroundPosition.x, + y: scrollbarBackgroundPosition.y + }); + scrollbarBarPosition.y = scrollbarBackgroundPosition.y + 1 + + scrollbarValue * (scrollbarBackgroundHeight - scrollbarBarHeight - 2); + Overlays.editOverlay(scrollbarBar, { + x: scrollbarBackgroundPosition.x + 1, + y: scrollbarBarPosition.y + }); + + + x = windowLeft + WINDOW_MARGIN; + y = windowPosition.y + - DISPLAY_SPACER + - windowLineHeight - VISIBILITY_SPACER + - windowLineHeight - WINDOW_BASE_MARGIN; + if (shouldShowFriendsButton()) { + y -= FRIENDS_BUTTON_HEIGHT; + Overlays.editOverlay(friendsButton, { + x: x, + y: y + }); + y += FRIENDS_BUTTON_HEIGHT; + } + + y += DISPLAY_SPACER; + displayControl.updatePosition(x, y); + + y += windowLineHeight + VISIBILITY_SPACER; + visibilityControl.updatePosition(x, y); + } + + function updateUsersDisplay() { + var displayText = "", + user, + userText, + textWidth, + maxTextWidth, + ellipsisWidth, + reducedTextWidth, + i; + + if (!isMinimized) { + maxTextWidth = WINDOW_WIDTH - (isUsingScrollbars ? SCROLLBAR_BACKGROUND_WIDTH : 0) - 2 * WINDOW_MARGIN; + ellipsisWidth = Overlays.textSize(windowPane, "...").width; + reducedTextWidth = maxTextWidth - ellipsisWidth; + + for (i = 0; i < numUsersToDisplay; i += 1) { + user = usersOnline[linesOfUsers[firstUserToDisplay + i]]; + userText = user.text; + textWidth = user.textWidth; + + if (textWidth > maxTextWidth) { + // Trim and append "..." to fit window width + maxTextWidth = maxTextWidth - Overlays.textSize(windowPane, "...").width; + while (textWidth > reducedTextWidth) { + userText = userText.slice(0, -1); + textWidth = Overlays.textSize(windowPane, userText).width; + } + userText += "..."; + } + + displayText += "\n" + userText; + } + + displayText = displayText.slice(1); // Remove leading "\n". + + scrollbarBackgroundHeight = numUsersToDisplay * windowLineHeight - windowLineSpacing / 2; + Overlays.editOverlay(scrollbarBackground, { + height: scrollbarBackgroundHeight, + visible: isLoggedIn && isUsingScrollbars + }); + scrollbarBarHeight = Math.max(numUsersToDisplay / linesOfUsers.length * scrollbarBackgroundHeight, + SCROLLBAR_BAR_MIN_HEIGHT); + Overlays.editOverlay(scrollbarBar, { + height: scrollbarBarHeight, + visible: isLoggedIn && isUsingScrollbars + }); + } + + Overlays.editOverlay(windowBorder, { + height: windowBorderHeight + }); + + Overlays.editOverlay(windowPane, { + height: windowHeight, + text: displayText + }); + + Overlays.editOverlay(windowHeading, { + text: isLoggedIn ? (linesOfUsers.length > 0 ? "Users online" : "No users online") : "Users online - log in to view" + }); + } + + function shouldShowFriendsButton() { + return isVisible && isLoggedIn && !isMinimized; + } + + function updateOverlayVisibility() { + Overlays.editOverlay(windowBorder, { + visible: isVisible && isBorderVisible + }); + Overlays.editOverlay(windowPane, { + visible: isVisible + }); + Overlays.editOverlay(windowHeading, { + visible: isVisible + }); + Overlays.editOverlay(minimizeButton, { + visible: isVisible + }); + Overlays.editOverlay(scrollbarBackground, { + visible: isVisible && isUsingScrollbars && !isMinimized + }); + Overlays.editOverlay(scrollbarBar, { + visible: isVisible && isUsingScrollbars && !isMinimized + }); + Overlays.editOverlay(friendsButton, { + visible: shouldShowFriendsButton() + }); + displayControl.setVisible(isVisible && !isMinimized); + visibilityControl.setVisible(isVisible && !isMinimized); + } + + function checkLoggedIn() { + var wasLoggedIn = isLoggedIn; + + isLoggedIn = Account.isLoggedIn(); + if (isLoggedIn !== wasLoggedIn) { + if (wasLoggedIn) { + setMinimized(true); + calculateWindowHeight(); + updateOverlayPositions(); + updateUsersDisplay(); + } + + updateOverlayVisibility(); + } + } + + function pollUsers() { + var url = API_URL; + + if (showMe === DISPLAY_FRIENDS) { + url += API_FRIENDS_FILTER; + } + + usersRequest = new XMLHttpRequest(); + usersRequest.open("GET", url, true); + usersRequest.timeout = HTTP_GET_TIMEOUT; + usersRequest.ontimeout = pollUsersTimedOut; + usersRequest.onreadystatechange = processUsers; + usersRequest.send(); + } + + processUsers = function () { + var response, + myUsername, + user, + userText, + i; + + if (usersRequest.readyState === usersRequest.DONE) { + if (usersRequest.status === 200) { + response = JSON.parse(usersRequest.responseText); + if (response.status !== "success") { + print("Error: Request for users status returned status = " + response.status); + usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + if (!response.hasOwnProperty("data") || !response.data.hasOwnProperty("users")) { + print("Error: Request for users status returned invalid data"); + usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + + usersOnline = response.data.users; + myUsername = GlobalServices.username; + linesOfUsers = []; + for (i = 0; i < usersOnline.length; i += 1) { + user = usersOnline[i]; + if (user.username !== myUsername && user.online) { + userText = user.username; + if (user.location.root) { + userText += " @ " + user.location.root.name; + } + + usersOnline[i].text = userText; + usersOnline[i].textWidth = Overlays.textSize(windowPane, userText).width; + + linesOfUsers.push(i); + } + } + + checkLoggedIn(); + calculateWindowHeight(); + updateUsersDisplay(); + updateOverlayPositions(); + + } else { + print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText); + usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + + usersTimer = Script.setTimeout(pollUsers, USERS_UPDATE_TIMEOUT); // Update after finished processing. + } + }; + + pollUsersTimedOut = function () { + print("Error: Request for users status timed out"); + usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + }; + + function setVisible(visible) { + isVisible = visible; + + if (isVisible) { + if (usersTimer === null) { + pollUsers(); + } + } else { + Script.clearTimeout(usersTimer); + usersTimer = null; + } + + updateOverlayVisibility(); + } + + function setMinimized(minimized) { + isMinimized = minimized; + Overlays.editOverlay(minimizeButton, { + subImage: { + y: isMinimized ? MIN_MAX_BUTTON_SVG_HEIGHT / 2 : 0 + } + }); + updateOverlayVisibility(); + Settings.setValue(SETTING_USERS_WINDOW_MINIMIZED, isMinimized); + } + + function onMenuItemEvent(event) { + if (event === MENU_ITEM) { + setVisible(Menu.isOptionChecked(MENU_ITEM)); + } + } + + function onFindableByChanged(event) { + if (VISIBILITY_VALUES.indexOf(event) !== -1) { + myVisibility = event; + visibilityControl.setValue(event); + Settings.setValue(SETTING_USERS_VISIBLE_TO, myVisibility); + } else { + print("Error: Unrecognized onFindableByChanged value: " + event); + } + } + + function onMousePressEvent(event) { + var clickedOverlay, + numLinesBefore, + overlayX, + overlayY, + minY, + maxY, + lineClicked, + userClicked, + delta; + + if (!isVisible || isWindowDisabled()) { + return; + } + + clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + + if (displayControl.handleClick(clickedOverlay)) { + if (usersTimer !== null) { + Script.clearTimeout(usersTimer); + usersTimer = null; + } + pollUsers(); + showMe = displayControl.getValue(); + Settings.setValue(SETTING_USERS_SHOW_ME, showMe); + return; + } + + if (visibilityControl.handleClick(clickedOverlay)) { + myVisibility = visibilityControl.getValue(); + GlobalServices.findableBy = myVisibility; + Settings.setValue(SETTING_USERS_VISIBLE_TO, myVisibility); + return; + } + + if (clickedOverlay === windowPane) { + + overlayX = event.x - windowPosition.x - WINDOW_MARGIN; + overlayY = event.y - windowPosition.y + windowHeight - WINDOW_MARGIN - windowLineHeight; + + numLinesBefore = Math.round(overlayY / windowLineHeight); + minY = numLinesBefore * windowLineHeight; + maxY = minY + windowTextHeight; + + lineClicked = -1; + if (minY <= overlayY && overlayY <= maxY) { + lineClicked = numLinesBefore; + } + + userClicked = firstUserToDisplay + lineClicked; + + if (0 <= userClicked && userClicked < linesOfUsers.length && 0 <= overlayX + && overlayX <= usersOnline[linesOfUsers[userClicked]].textWidth) { + //print("Go to " + usersOnline[linesOfUsers[userClicked]].username); + location.goToUser(usersOnline[linesOfUsers[userClicked]].username); + } + + return; + } + + if (clickedOverlay === minimizeButton) { + setMinimized(!isMinimized); + calculateWindowHeight(); + updateOverlayPositions(); + updateUsersDisplay(); + return; + } + + if (clickedOverlay === scrollbarBar) { + scrollbarBarClickedAt = (event.y - scrollbarBarPosition.y) / scrollbarBarHeight; + Overlays.editOverlay(scrollbarBar, { + backgroundAlpha: SCROLLBAR_BAR_SELECTED_ALPHA + }); + isMovingScrollbar = true; + return; + } + + if (clickedOverlay === scrollbarBackground) { + delta = scrollbarBarHeight / (scrollbarBackgroundHeight - scrollbarBarHeight); + + if (event.y < scrollbarBarPosition.y) { + scrollbarValue = Math.max(scrollbarValue - delta, 0.0); + } else { + scrollbarValue = Math.min(scrollbarValue + delta, 1.0); + } + + firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); + updateOverlayPositions(); + updateUsersDisplay(); + return; + } + + if (clickedOverlay === friendsButton) { + if (!friendsWindow) { + 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(); + return; + } + + if (clickedOverlay === windowBorder) { + movingClickOffset = { + x: event.x - windowPosition.x, + y: event.y - windowPosition.y + }; + + isMovingWindow = true; + } + } + + function onMouseMoveEvent(event) { + var isVisible; + + if (!isLoggedIn || isWindowDisabled()) { + return; + } + + if (isMovingScrollbar) { + if (scrollbarBackgroundPosition.x - WINDOW_MARGIN <= event.x + && event.x <= scrollbarBackgroundPosition.x + SCROLLBAR_BACKGROUND_WIDTH + WINDOW_MARGIN + && scrollbarBackgroundPosition.y - WINDOW_MARGIN <= event.y + && event.y <= scrollbarBackgroundPosition.y + scrollbarBackgroundHeight + WINDOW_MARGIN) { + scrollbarValue = (event.y - scrollbarBarClickedAt * scrollbarBarHeight - scrollbarBackgroundPosition.y) + / (scrollbarBackgroundHeight - scrollbarBarHeight - 2); + scrollbarValue = Math.min(Math.max(scrollbarValue, 0.0), 1.0); + firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); + updateOverlayPositions(); + updateUsersDisplay(); + } else { + Overlays.editOverlay(scrollbarBar, { + backgroundAlpha: SCROLLBAR_BAR_ALPHA + }); + isMovingScrollbar = false; + } + } + + if (isMovingWindow) { + windowPosition = { + x: event.x - movingClickOffset.x, + y: event.y - movingClickOffset.y + }; + + saturateWindowPosition(); + calculateWindowHeight(); + updateOverlayPositions(); + updateUsersDisplay(); + + } else { + + isVisible = isBorderVisible; + if (isVisible) { + isVisible = windowPosition.x - WINDOW_BORDER_LEFT_MARGIN <= event.x + && event.x <= windowPosition.x - WINDOW_BORDER_LEFT_MARGIN + WINDOW_BORDER_WIDTH + && windowPosition.y - windowHeight - WINDOW_BORDER_TOP_MARGIN <= event.y + && event.y <= windowPosition.y + WINDOW_BORDER_BOTTOM_MARGIN; + } else { + isVisible = windowPosition.x <= event.x && event.x <= windowPosition.x + WINDOW_WIDTH + && windowPosition.y - windowHeight <= event.y && event.y <= windowPosition.y; + } + if (isVisible !== isBorderVisible) { + isBorderVisible = isVisible; + Overlays.editOverlay(windowBorder, { + visible: isBorderVisible + }); + } + } + } + + function onMouseReleaseEvent() { + var offset = {}; + + if (isWindowDisabled()) { + return; + } + + if (isMovingScrollbar) { + Overlays.editOverlay(scrollbarBar, { + backgroundAlpha: SCROLLBAR_BAR_ALPHA + }); + isMovingScrollbar = false; + } + + if (isMovingWindow) { + // Save offset of bottom of window to nearest edge of the window. + offset.x = (windowPosition.x + WINDOW_WIDTH / 2 < viewport.x / 2) + ? windowPosition.x : windowPosition.x - viewport.x; + offset.y = (windowPosition.y < viewport.y / 2) + ? windowPosition.y : windowPosition.y - viewport.y; + Settings.setValue(SETTING_USERS_WINDOW_OFFSET, JSON.stringify(offset)); + isMovingWindow = false; + } + } + + function onScriptUpdate() { + var oldViewport = viewport, + oldIsMirrorDisplay = isMirrorDisplay, + oldIsFullscreenMirror = isFullscreenMirror, + MIRROR_MENU_ITEM = "Mirror", + FULLSCREEN_MIRROR_MENU_ITEM = "Fullscreen Mirror"; + + if (isWindowDisabled()) { + return; + } + + viewport = Controller.getViewportDimensions(); + isMirrorDisplay = Menu.isOptionChecked(MIRROR_MENU_ITEM); + isFullscreenMirror = Menu.isOptionChecked(FULLSCREEN_MIRROR_MENU_ITEM); + + if (viewport.y !== oldViewport.y || isMirrorDisplay !== oldIsMirrorDisplay + || isFullscreenMirror !== oldIsFullscreenMirror) { + calculateWindowHeight(); + updateUsersDisplay(); + } + + if (viewport.y !== oldViewport.y) { + if (windowPosition.y > oldViewport.y / 2) { + // Maintain position w.r.t. bottom of window. + windowPosition.y = viewport.y - (oldViewport.y - windowPosition.y); + } + } + + if (viewport.x !== oldViewport.x) { + if (windowPosition.x + (WINDOW_WIDTH / 2) > oldViewport.x / 2) { + // Maintain position w.r.t. right of window. + windowPosition.x = viewport.x - (oldViewport.x - windowPosition.x); + } + } + + updateOverlayPositions(); + } + + function setUp() { + var textSizeOverlay, + offsetSetting, + offset = {}, + hmdViewport; + + textSizeOverlay = Overlays.addOverlay("text", { + font: WINDOW_FONT, + visible: false + }); + windowTextHeight = Math.floor(Overlays.textSize(textSizeOverlay, "1").height); + windowLineSpacing = Math.floor(Overlays.textSize(textSizeOverlay, "1\n2").height - 2 * windowTextHeight); + windowLineHeight = windowTextHeight + windowLineSpacing; + windowMinimumHeight = windowTextHeight + WINDOW_MARGIN + WINDOW_BASE_MARGIN; + Overlays.deleteOverlay(textSizeOverlay); + + viewport = Controller.getViewportDimensions(); + + offsetSetting = Settings.getValue(SETTING_USERS_WINDOW_OFFSET); + if (offsetSetting !== "") { + offset = JSON.parse(Settings.getValue(SETTING_USERS_WINDOW_OFFSET)); + } + if (offset.hasOwnProperty("x") && offset.hasOwnProperty("y")) { + windowPosition.x = offset.x < 0 ? viewport.x + offset.x : offset.x; + windowPosition.y = offset.y <= 0 ? viewport.y + offset.y : offset.y; + } else { + hmdViewport = Controller.getRecommendedOverlayRect(); + windowPosition = { + x: (viewport.x - hmdViewport.width) / 2, // HMD viewport is narrower than screen. + y: hmdViewport.height // HMD viewport starts at top of screen but only extends down so far. + }; + } + + saturateWindowPosition(); + calculateWindowHeight(); + + windowBorder = Overlays.addOverlay("rectangle", { + x: 0, + y: viewport.y, // Start up off-screen + width: WINDOW_BORDER_WIDTH, + height: windowBorderHeight, + radius: WINDOW_BORDER_RADIUS, + color: WINDOW_BORDER_COLOR, + alpha: WINDOW_BORDER_ALPHA, + visible: false + }); + + windowPane = Overlays.addOverlay("text", { + x: 0, + y: viewport.y, + width: WINDOW_WIDTH, + height: windowHeight, + topMargin: WINDOW_MARGIN + windowLineHeight, + leftMargin: WINDOW_MARGIN, + color: WINDOW_FOREGROUND_COLOR, + alpha: WINDOW_FOREGROUND_ALPHA, + backgroundColor: WINDOW_BACKGROUND_COLOR, + backgroundAlpha: WINDOW_BACKGROUND_ALPHA, + text: "", + font: WINDOW_FONT, + visible: false + }); + + windowHeading = Overlays.addOverlay("text", { + x: 0, + y: viewport.y, + width: WINDOW_WIDTH - 2 * WINDOW_MARGIN, + height: windowTextHeight, + topMargin: 0, + leftMargin: 0, + color: WINDOW_HEADING_COLOR, + alpha: WINDOW_HEADING_ALPHA, + backgroundAlpha: 0.0, + text: "Users online", + font: WINDOW_FONT, + visible: false + }); + + minimizeButton = Overlays.addOverlay("image", { + x: 0, + y: viewport.y, + width: MIN_MAX_BUTTON_WIDTH, + height: MIN_MAX_BUTTON_HEIGHT, + imageURL: MIN_MAX_BUTTON_SVG, + subImage: { + x: 0, + y: 0, + width: MIN_MAX_BUTTON_SVG_WIDTH, + height: MIN_MAX_BUTTON_SVG_HEIGHT / 2 + }, + color: MIN_MAX_BUTTON_COLOR, + alpha: MIN_MAX_BUTTON_ALPHA, + visible: false + }); + + scrollbarBackgroundPosition = { + x: 0, + y: viewport.y + }; + scrollbarBackground = Overlays.addOverlay("text", { + x: 0, + y: scrollbarBackgroundPosition.y, + width: SCROLLBAR_BACKGROUND_WIDTH, + height: windowTextHeight, + backgroundColor: SCROLLBAR_BACKGROUND_COLOR, + backgroundAlpha: SCROLLBAR_BACKGROUND_ALPHA, + text: "", + visible: false + }); + + scrollbarBarPosition = { + x: 0, + y: viewport.y + }; + scrollbarBar = Overlays.addOverlay("text", { + x: 0, + y: scrollbarBarPosition.y, + width: SCROLLBAR_BACKGROUND_WIDTH - 2, + height: windowTextHeight, + backgroundColor: SCROLLBAR_BAR_COLOR, + backgroundAlpha: SCROLLBAR_BAR_ALPHA, + text: "", + visible: false + }); + + friendsButton = Overlays.addOverlay("image", { + x: 0, + y: viewport.y, + width: FRIENDS_BUTTON_WIDTH, + height: FRIENDS_BUTTON_HEIGHT, + imageURL: FRIENDS_BUTTON_SVG, + subImage: { + x: 0, + y: 0, + width: FRIENDS_BUTTON_SVG_WIDTH, + height: FRIENDS_BUTTON_SVG_HEIGHT + }, + color: FRIENDS_BUTTON_COLOR, + alpha: FRIENDS_BUTTON_ALPHA, + visible: false + }); + + showMe = Settings.getValue(SETTING_USERS_SHOW_ME, ""); + if (DISPLAY_VALUES.indexOf(showMe) === -1) { + showMe = DISPLAY_EVERYONE; + } + + displayControl = new PopUpMenu({ + prompt: DISPLAY_PROMPT, + value: showMe, + values: DISPLAY_VALUES, + displayValues: DISPLAY_DISPLAY_VALUES, + x: 0, + y: viewport.y, + width: WINDOW_WIDTH - 1.5 * WINDOW_MARGIN, + promptWidth: DISPLAY_PROMPT_WIDTH, + lineHeight: windowLineHeight, + textHeight: windowTextHeight, + font: WINDOW_FONT, + promptColor: WINDOW_HEADING_COLOR, + promptAlpha: WINDOW_HEADING_ALPHA, + promptBackgroundColor: WINDOW_BACKGROUND_COLOR, + promptBackgroundAlpha: 0.0, + optionColor: WINDOW_FOREGROUND_COLOR, + optionAlpha: WINDOW_FOREGROUND_ALPHA, + optionBackgroundColor: OPTION_BACKGROUND_COLOR, + optionBackgroundAlpha: OPTION_BACKGROUND_ALPHA, + popupBackgroundColor: DISPLAY_OPTIONS_BACKGROUND_COLOR, + popupBackgroundAlpha: DISPLAY_OPTIONS_BACKGROUND_ALPHA, + buttonColor: MIN_MAX_BUTTON_COLOR, + buttonAlpha: MIN_MAX_BUTTON_ALPHA, + visible: false + }); + + myVisibility = Settings.getValue(SETTING_USERS_VISIBLE_TO, ""); + if (VISIBILITY_VALUES.indexOf(myVisibility) === -1) { + myVisibility = VISIBILITY_FRIENDS; + } + GlobalServices.findableBy = myVisibility; + + visibilityControl = new PopUpMenu({ + prompt: VISIBILITY_PROMPT, + value: myVisibility, + values: VISIBILITY_VALUES, + displayValues: VISIBILITY_DISPLAY_VALUES, + x: 0, + y: viewport.y, + width: WINDOW_WIDTH - 1.5 * WINDOW_MARGIN, + promptWidth: VISIBILITY_PROMPT_WIDTH, + lineHeight: windowLineHeight, + textHeight: windowTextHeight, + font: WINDOW_FONT, + promptColor: WINDOW_HEADING_COLOR, + promptAlpha: WINDOW_HEADING_ALPHA, + promptBackgroundColor: WINDOW_BACKGROUND_COLOR, + promptBackgroundAlpha: 0.0, + optionColor: WINDOW_FOREGROUND_COLOR, + optionAlpha: WINDOW_FOREGROUND_ALPHA, + optionBackgroundColor: OPTION_BACKGROUND_COLOR, + optionBackgroundAlpha: OPTION_BACKGROUND_ALPHA, + popupBackgroundColor: DISPLAY_OPTIONS_BACKGROUND_COLOR, + popupBackgroundAlpha: DISPLAY_OPTIONS_BACKGROUND_ALPHA, + buttonColor: MIN_MAX_BUTTON_COLOR, + buttonAlpha: MIN_MAX_BUTTON_ALPHA, + visible: false + }); + + Controller.mousePressEvent.connect(onMousePressEvent); + Controller.mouseMoveEvent.connect(onMouseMoveEvent); + Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); + + Menu.addMenuItem({ + menuName: MENU_NAME, + menuItemName: MENU_ITEM, + afterItem: MENU_ITEM_AFTER, + isCheckable: true, + isChecked: isVisible + }); + Menu.menuItemEvent.connect(onMenuItemEvent); + + GlobalServices.findableByChanged.connect(onFindableByChanged); + + Script.update.connect(onScriptUpdate); + + pollUsers(); + + // Set minimized at end - setup code does not handle `minimized == false` correctly + setMinimized(isValueTrue(Settings.getValue(SETTING_USERS_WINDOW_MINIMIZED, true))); + } + + function tearDown() { + Menu.removeMenuItem(MENU_NAME, MENU_ITEM); + + Script.clearTimeout(usersTimer); + Overlays.deleteOverlay(windowBorder); + Overlays.deleteOverlay(windowPane); + Overlays.deleteOverlay(windowHeading); + Overlays.deleteOverlay(minimizeButton); + Overlays.deleteOverlay(scrollbarBackground); + Overlays.deleteOverlay(scrollbarBar); + Overlays.deleteOverlay(friendsButton); + displayControl.tearDown(); + visibilityControl.tearDown(); + } + + setUp(); + Script.scriptEnding.connect(tearDown); +}()); + +}()); // END LOCAL_SCOPE From f18094f5654c57dde49e1c54147964188be35f33 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 25 Jan 2017 12:00:34 -0800 Subject: [PATCH 2/4] make use of tablet or HUD depend on a single variable. avoid some duplicated code --- .../src/scripting/HMDScriptingInterface.h | 10 +- .../scripting/ToolbarScriptingInterface.cpp | 4 + scripts/defaultScripts.js | 2 + scripts/defaultScriptsHUD.js | 131 -- scripts/system/bubble.js | 32 +- scripts/system/bubbleHUD.js | 195 --- scripts/system/edit.js | 35 +- scripts/system/goto.js | 41 +- scripts/system/gotoHUD.js | 46 - scripts/system/help.js | 41 +- scripts/system/helpHUD.js | 41 - scripts/system/hmd.js | 63 +- scripts/system/hmdHUD.js | 79 -- scripts/system/marketplaces/marketplaceHUD.js | 131 -- scripts/system/marketplaces/marketplaces.js | 115 +- .../system/marketplaces/marketplacesHUD.js | 233 ---- scripts/system/mute.js | 43 +- scripts/system/muteHUD.js | 51 - scripts/system/pal.js | 33 +- scripts/system/palHUD.js | 658 --------- scripts/system/snapshot.js | 41 +- scripts/system/snapshotHUD.js | 226 --- scripts/system/tablet-ui/tabletUI.js | 2 +- scripts/system/users.js | 104 +- scripts/system/usersHUD.js | 1237 ----------------- 25 files changed, 406 insertions(+), 3188 deletions(-) delete mode 100644 scripts/defaultScriptsHUD.js delete mode 100644 scripts/system/bubbleHUD.js delete mode 100644 scripts/system/gotoHUD.js delete mode 100644 scripts/system/helpHUD.js delete mode 100644 scripts/system/hmdHUD.js delete mode 100644 scripts/system/marketplaces/marketplaceHUD.js delete mode 100644 scripts/system/marketplaces/marketplacesHUD.js delete mode 100644 scripts/system/muteHUD.js delete mode 100644 scripts/system/palHUD.js delete mode 100644 scripts/system/snapshotHUD.js delete mode 100644 scripts/system/usersHUD.js diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index bbf446456d..a8c1600a16 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -31,7 +31,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen Q_PROPERTY(bool showTablet READ getShouldShowTablet) Q_PROPERTY(QUuid tabletID READ getCurrentTableUIID WRITE setCurrentTabletUIID) Q_PROPERTY(unsigned int homeButtonID READ getCurrentHomeButtonUUID WRITE setCurrentHomeButtonUUID) - + Q_PROPERTY(bool hudUIEnabled READ getHUDUIEnabled WRITE setHUDUIEnabled); public: Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const; @@ -91,11 +91,13 @@ public: bool getShouldShowTablet() const { return _showTablet; } void setCurrentTabletUIID(QUuid tabletID) { _tabletUIID = tabletID; } - QUuid getCurrentTableUIID() { return _tabletUIID; } + QUuid getCurrentTableUIID() const { return _tabletUIID; } void setCurrentHomeButtonUUID(unsigned int homeButtonID) { _homeButtonID = homeButtonID; } - unsigned int getCurrentHomeButtonUUID() { return _homeButtonID; } + unsigned int getCurrentHomeButtonUUID() const { return _homeButtonID; } + bool getHUDUIEnabled() const { return _hudUIEnabled; } + void setHUDUIEnabled(bool value) { _hudUIEnabled = value; } private: bool _showTablet { false }; @@ -112,6 +114,8 @@ private: bool getHUDLookAtPosition3D(glm::vec3& result) const; glm::mat4 getWorldHMDMatrix() const; std::atomic _showHandControllersCount { 0 }; + + bool _hudUIEnabled; }; #endif // hifi_HMDScriptingInterface_h diff --git a/interface/src/scripting/ToolbarScriptingInterface.cpp b/interface/src/scripting/ToolbarScriptingInterface.cpp index 1f2228c6d9..eb60ff0f67 100644 --- a/interface/src/scripting/ToolbarScriptingInterface.cpp +++ b/interface/src/scripting/ToolbarScriptingInterface.cpp @@ -22,6 +22,10 @@ public: connect(qmlObject, SIGNAL(clicked()), this, SIGNAL(clicked())); } + Q_INVOKABLE void editProperties(QVariantMap properties) { + qDebug() << "XXX WRITE TabletButtonProxy::editProperties"; + } + signals: void clicked(); }; diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index bd3131f4ff..f046351147 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -43,6 +43,8 @@ var DEFAULT_SCRIPTS = [ var MENU_CATEGORY = "Developer"; var MENU_ITEM = "Debug defaultScripts.js"; +HMD.hudUIEnabled = true; + var SETTINGS_KEY = '_debugDefaultScriptsIsChecked'; var previousSetting = Settings.getValue(SETTINGS_KEY); diff --git a/scripts/defaultScriptsHUD.js b/scripts/defaultScriptsHUD.js deleted file mode 100644 index 098a74ffd0..0000000000 --- a/scripts/defaultScriptsHUD.js +++ /dev/null @@ -1,131 +0,0 @@ -"use strict"; -/* jslint vars: true, plusplus: true */ - -// -// defaultScripts.js -// examples -// -// Copyright 2014 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 -// - -var DEFAULT_SCRIPTS = [ - "system/progress.js", - "system/away.js", - "system/usersHUD.js", - "system/muteHUD.js", - "system/gotoHUD.js", - "system/hmdHUD.js", - "system/marketplaces/marketplacesHUD.js", - "system/edit.js", - "system/palHUD.js", //"system/mod.js", // older UX, if you prefer - "system/selectAudioDevice.js", - "system/notifications.js", - "system/controllers/controllerDisplayManager.js", - "system/controllers/handControllerGrab.js", - "system/controllers/handControllerPointer.js", - "system/controllers/squeezeHands.js", - "system/controllers/grab.js", - "system/controllers/teleport.js", - "system/controllers/toggleAdvancedMovementForHandControllers.js", - "system/dialTone.js", - "system/firstPersonHMD.js", - "system/snapshotHUD.js", - "system/helpHUD.js", - "system/bubbleHUD.js" -]; - -// add a menu item for debugging -var MENU_CATEGORY = "Developer"; -var MENU_ITEM = "Debug defaultScripts.js"; - -var SETTINGS_KEY = '_debugDefaultScriptsIsChecked'; -var previousSetting = Settings.getValue(SETTINGS_KEY); - -if (previousSetting === '' || previousSetting === false || previousSetting === 'false') { - previousSetting = false; -} - -if (previousSetting === true || previousSetting === 'true') { - previousSetting = true; -} - - - - -if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_ITEM)) { - Menu.addMenuItem({ - menuName: MENU_CATEGORY, - menuItemName: MENU_ITEM, - isCheckable: true, - isChecked: previousSetting, - grouping: "Advanced" - }); -} - -function runDefaultsTogether() { - for (var j in DEFAULT_SCRIPTS) { - Script.include(DEFAULT_SCRIPTS[j]); - } -} - -function runDefaultsSeparately() { - for (var i in DEFAULT_SCRIPTS) { - Script.load(DEFAULT_SCRIPTS[i]); - } -} -// start all scripts -if (Menu.isOptionChecked(MENU_ITEM)) { - // we're debugging individual default scripts - // so we load each into its own ScriptEngine instance - debuggingDefaultScripts = true; - runDefaultsSeparately(); -} else { - // include all default scripts into this ScriptEngine - runDefaultsTogether(); -} - -function menuItemEvent(menuItem) { - if (menuItem == MENU_ITEM) { - - isChecked = Menu.isOptionChecked(MENU_ITEM); - if (isChecked === true) { - Settings.setValue(SETTINGS_KEY, true); - } else if (isChecked === false) { - Settings.setValue(SETTINGS_KEY, false); - } - Window.alert('You must reload all scripts for this to take effect.') - } - - -} - - - -function stopLoadedScripts() { - // remove debug script loads - var runningScripts = ScriptDiscoveryService.getRunning(); - for (var i in runningScripts) { - var scriptName = runningScripts[i].name; - for (var j in DEFAULT_SCRIPTS) { - if (DEFAULT_SCRIPTS[j].slice(-scriptName.length) === scriptName) { - ScriptDiscoveryService.stopScript(runningScripts[i].url); - } - } - } -} - -function removeMenuItem() { - if (!Menu.isOptionChecked(MENU_ITEM)) { - Menu.removeMenuItem(MENU_CATEGORY, MENU_ITEM); - } -} - -Script.scriptEnding.connect(function() { - stopLoadedScripts(); - removeMenuItem(); -}); - -Menu.menuItemEvent.connect(menuItemEvent); diff --git a/scripts/system/bubble.js b/scripts/system/bubble.js index e787793eb2..4d93926abb 100644 --- a/scripts/system/bubble.js +++ b/scripts/system/bubble.js @@ -15,8 +15,7 @@ (function () { // BEGIN LOCAL_SCOPE - // grab the toolbar - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button; // Used for animating and disappearing the bubble var bubbleOverlayTimestamp; // Used for flashing the HUD button upon activation @@ -164,11 +163,23 @@ } } - // Setup the bubble button and add it to the toolbar - var button = tablet.addButton({ - icon: "icons/tablet-icons/bubble-i.svg", - text: "BUBBLE" - }); + // Setup the bubble button + var buttonName = "BUBBLE"; + if (HMD.hudUIEnabled) { + var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + button = toolbar.addButton({ + objectName: 'bubble', + imageURL: buttonImageURL(), + visible: true, + alpha: 0.9 + }); + } else { + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + button = tablet.addButton({ + icon: "icons/tablet-icons/bubble-i.svg", + text: buttonName + }); + } onBubbleToggled(); button.clicked.connect(Users.toggleIgnoreRadius); @@ -178,7 +189,12 @@ // Cleanup the toolbar button and overlays when script is stopped Script.scriptEnding.connect(function () { button.clicked.disconnect(Users.toggleIgnoreRadius); - tablet.removeButton(button); + if (tablet) { + tablet.removeButton(button); + } + if (toolbar) { + toolbar.removeButton('bubble'); + } Users.ignoreRadiusEnabledChanged.disconnect(onBubbleToggled); Users.enteredIgnoreRadius.disconnect(enteredIgnoreRadius); Overlays.deleteOverlay(bubbleOverlay); diff --git a/scripts/system/bubbleHUD.js b/scripts/system/bubbleHUD.js deleted file mode 100644 index 2f7286872e..0000000000 --- a/scripts/system/bubbleHUD.js +++ /dev/null @@ -1,195 +0,0 @@ -"use strict"; - -// -// bubble.js -// scripts/system/ -// -// Created by Brad Hefta-Gaub on 11/18/2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -/* global Toolbars, Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */ - - -(function () { // BEGIN LOCAL_SCOPE - - // grab the toolbar - var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - // Used for animating and disappearing the bubble - var bubbleOverlayTimestamp; - // Used for flashing the HUD button upon activation - var bubbleButtonFlashState = false; - // Used for flashing the HUD button upon activation - var bubbleButtonTimestamp; - // Affects bubble height - const BUBBLE_HEIGHT_SCALE = 0.15; - // The bubble model itself - var bubbleOverlay = Overlays.addOverlay("model", { - url: Script.resolvePath("assets/models/Bubble-v14.fbx"), // If you'd like to change the model, modify this line (and the dimensions below) - dimensions: { x: 1.0, y: 0.75, z: 1.0 }, - position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z }, - rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), - scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 }, - visible: false, - ignoreRayIntersection: true - }); - // The bubble activation sound - var bubbleActivateSound = SoundCache.getSound(Script.resolvePath("assets/sounds/bubble.wav")); - // Is the update() function connected? - var updateConnected = false; - - const BUBBLE_VISIBLE_DURATION_MS = 3000; - const BUBBLE_RAISE_ANIMATION_DURATION_MS = 750; - const BUBBLE_HUD_ICON_FLASH_INTERVAL_MS = 500; - - var ASSETS_PATH = Script.resolvePath("assets"); - var TOOLS_PATH = Script.resolvePath("assets/images/tools/"); - - function buttonImageURL() { - return TOOLS_PATH + 'bubble.svg'; - } - - // Hides the bubble model overlay and resets the button flash state - function hideOverlays() { - Overlays.editOverlay(bubbleOverlay, { - visible: false - }); - bubbleButtonFlashState = false; - } - - // Make the bubble overlay visible, set its position, and play the sound - function createOverlays() { - Audio.playSound(bubbleActivateSound, { - position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }, - localOnly: true, - volume: 0.2 - }); - hideOverlays(); - if (updateConnected === true) { - updateConnected = false; - Script.update.disconnect(update); - } - - Overlays.editOverlay(bubbleOverlay, { - position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z }, - rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), - scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 }, - visible: true - }); - bubbleOverlayTimestamp = Date.now(); - bubbleButtonTimestamp = bubbleOverlayTimestamp; - Script.update.connect(update); - updateConnected = true; - } - - // Called from the C++ scripting interface to show the bubble overlay - function enteredIgnoreRadius() { - createOverlays(); - } - - // Used to set the state of the bubble HUD button - function writeButtonProperties(parameter) { - button.writeProperty('buttonState', parameter ? 0 : 1); - button.writeProperty('defaultState', parameter ? 0 : 1); - button.writeProperty('hoverState', parameter ? 2 : 3); - } - - // The bubble script's update function - update = function () { - var timestamp = Date.now(); - var delay = (timestamp - bubbleOverlayTimestamp); - var overlayAlpha = 1.0 - (delay / BUBBLE_VISIBLE_DURATION_MS); - if (overlayAlpha > 0) { - // Flash button - if ((timestamp - bubbleButtonTimestamp) >= BUBBLE_VISIBLE_DURATION_MS) { - writeButtonProperties(bubbleButtonFlashState); - bubbleButtonTimestamp = timestamp; - bubbleButtonFlashState = !bubbleButtonFlashState; - } - - if (delay < BUBBLE_RAISE_ANIMATION_DURATION_MS) { - Overlays.editOverlay(bubbleOverlay, { - // Quickly raise the bubble from the ground up - position: { - x: MyAvatar.position.x, - y: (-((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, - z: MyAvatar.position.z - }, - rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), - scale: { - x: 2, - y: ((1 - ((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 0.5 + 0.5), - z: 2 - } - }); - } else { - // Keep the bubble in place for a couple seconds - Overlays.editOverlay(bubbleOverlay, { - position: { - x: MyAvatar.position.x, - y: MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, - z: MyAvatar.position.z - }, - rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), - scale: { - x: 2, - y: MyAvatar.scale * 0.5 + 0.5, - z: 2 - } - }); - } - } else { - hideOverlays(); - if (updateConnected === true) { - Script.update.disconnect(update); - updateConnected = false; - } - var bubbleActive = Users.getIgnoreRadiusEnabled(); - writeButtonProperties(bubbleActive); - } - }; - - // When the space bubble is toggled... - function onBubbleToggled() { - var bubbleActive = Users.getIgnoreRadiusEnabled(); - writeButtonProperties(bubbleActive); - if (bubbleActive) { - createOverlays(); - } else { - hideOverlays(); - if (updateConnected === true) { - Script.update.disconnect(update); - updateConnected = false; - } - } - } - - // Setup the bubble button and add it to the toolbar - var button = toolbar.addButton({ - objectName: 'bubble', - imageURL: buttonImageURL(), - visible: true, - alpha: 0.9 - }); - onBubbleToggled(); - - button.clicked.connect(Users.toggleIgnoreRadius); - Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled); - Users.enteredIgnoreRadius.connect(enteredIgnoreRadius); - - // Cleanup the toolbar button and overlays when script is stopped - Script.scriptEnding.connect(function () { - toolbar.removeButton('bubble'); - button.clicked.disconnect(Users.toggleIgnoreRadius); - Users.ignoreRadiusEnabledChanged.disconnect(onBubbleToggled); - Users.enteredIgnoreRadius.disconnect(enteredIgnoreRadius); - Overlays.deleteOverlay(bubbleOverlay); - bubbleButtonFlashState = false; - if (updateConnected === true) { - Script.update.disconnect(update); - } - }); - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/edit.js b/scripts/system/edit.js index ee16b683b3..075bc4360d 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -170,8 +170,9 @@ var toolBar = (function () { var EDIT_SETTING = "io.highfidelity.isEditting"; // for communication with other scripts var that = {}, toolBar, - activeButton, - tablet; + activeButton = null, + systemToolbar = null, + tablet = null; function createNewEntity(properties) { Settings.setValue(EDIT_SETTING, false); @@ -196,7 +197,12 @@ var toolBar = (function () { function cleanup() { that.setActive(false); - tablet.removeButton(activeButton); + if (tablet) { + tablet.removeButton(activeButton); + } + if (systemToolbar) { + systemToolbar.removeButton(EDIT_TOGGLE_BUTTON); + } } function addButton(name, image, handler) { @@ -231,11 +237,24 @@ var toolBar = (function () { }); - tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - activeButton = tablet.addButton({ - icon: "icons/tablet-icons/edit-i.svg", - text: "EDIT" - }); + if (HMD.hudUIEnabled) { + systemToolbar = Toolbars.getToolbar(SYSTEM_TOOLBAR); + activeButton = systemToolbar.addButton({ + objectName: EDIT_TOGGLE_BUTTON, + imageURL: TOOLS_PATH + "edit.svg", + visible: true, + alpha: 0.9, + buttonState: 1, + hoverState: 3, + defaultState: 1 + }); + } else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + activeButton = tablet.addButton({ + icon: "icons/tablet-icons/edit-i.svg", + text: "EDIT" + }); + } activeButton.clicked.connect(function() { that.toggle(); diff --git a/scripts/system/goto.js b/scripts/system/goto.js index 2051c2a9b0..b9ae33339b 100644 --- a/scripts/system/goto.js +++ b/scripts/system/goto.js @@ -10,32 +10,53 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* globals Tablet, Toolbars, Script, HMD, DialogsManager */ (function() { // BEGIN LOCAL_SCOPE -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -var button = tablet.addButton({ - icon: "icons/tablet-icons/goto-i.svg", - text:"GOTO" -}); - +var button; +var buttonName = "GOTO"; +var toolBar = null; +var tablet = null; function onAddressBarShown(visible) { button.editProperties({isActive: visible}); } -function setActive(active) { - isActive = active; -} function onClicked(){ DialogsManager.toggleAddressBar(); } + +if (HMD.hudUIEnabled) { + toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/directory.svg"), + visible: true, + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 + }); +} else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + button = tablet.addButton({ + icon: "icons/tablet-icons/goto-i.svg", + text: buttonName + }); +} + button.clicked.connect(onClicked); DialogsManager.addressBarShown.connect(onAddressBarShown); Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); - tablet.removeButton(button); + if (tablet) { + tablet.removeButton(button); + } + if (toolBar) { + toolBar.removeButton(buttonName); + } DialogsManager.addressBarShown.disconnect(onAddressBarShown); }); diff --git a/scripts/system/gotoHUD.js b/scripts/system/gotoHUD.js deleted file mode 100644 index 9116142293..0000000000 --- a/scripts/system/gotoHUD.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; - -// -// goto.js -// scripts/system/ -// -// Created by Howard Stearns on 2 Jun 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - - -var button = toolBar.addButton({ - objectName: "goto", - imageURL: Script.resolvePath("assets/images/tools/directory.svg"), - visible: true, - buttonState: 1, - defaultState: 1, - hoverState: 3, - alpha: 0.9, -}); - -function onAddressBarShown(visible) { - button.writeProperty('buttonState', visible ? 0 : 1); - button.writeProperty('defaultState', visible ? 0 : 1); - button.writeProperty('hoverState', visible ? 2 : 3); -} -function onClicked(){ - DialogsManager.toggleAddressBar(); -} -button.clicked.connect(onClicked); -DialogsManager.addressBarShown.connect(onAddressBarShown); - -Script.scriptEnding.connect(function () { - toolBar.removeButton("goto"); - button.clicked.disconnect(onClicked); - DialogsManager.addressBarShown.disconnect(onAddressBarShown); -}); - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/help.js b/scripts/system/help.js index a83ac24595..483a1c3dc4 100644 --- a/scripts/system/help.js +++ b/scripts/system/help.js @@ -10,19 +10,34 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* globals Tablet */ +/* globals Tablet, Toolbars, Script, HMD, Controller, Menu */ (function() { // BEGIN LOCAL_SCOPE - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - var button = tablet.addButton({ - icon: "icons/tablet-icons/help-i.svg", - text: "HELP" - }); + var button; + var buttonName = "HELP"; + var toolBar = null; + var tablet = null; + if (HMD.hudUIEnabled) { + toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/help.svg"), + visible: true, + hoverState: 2, + defaultState: 1, + buttonState: 1, + alpha: 0.9 + }); + } else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + button = tablet.addButton({ + icon: "icons/tablet-icons/help-i.svg", + text: buttonName + }); + } var enabled = false; function onClicked() { - var HELP_URL = Script.resourcesPath() + "html/help.html"; - // Similar logic to Application::showHelp() var defaultTab = "kbm"; var handControllerName = "vive"; @@ -37,8 +52,6 @@ } else if ("SDL2" in Controller.Hardware) { defaultTab = "gamepad"; } - var queryParameters = "handControllerName=" + handControllerName + "&defaultTab=" + defaultTab; - print("Help enabled " + Menu.isMenuEnabled("Help...")) if (enabled) { Menu.closeInfoView('InfoView_html/help.html'); @@ -63,8 +76,14 @@ }, POLL_RATE); Script.scriptEnding.connect(function () { + button.clicked.disconnect(onClicked); Script.clearInterval(interval); - tablet.removeButton(button); + if (tablet) { + tablet.removeButton(button); + } + if (toolBar) { + toolBar.removeButton(buttonName); + } }); }()); // END LOCAL_SCOPE diff --git a/scripts/system/helpHUD.js b/scripts/system/helpHUD.js deleted file mode 100644 index e79ed0444c..0000000000 --- a/scripts/system/helpHUD.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; - -// -// help.js -// scripts/system/ -// -// Created by Howard Stearns on 2 Nov 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - - var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - var buttonName = "help"; // matching location reserved in Desktop.qml - var button = toolBar.addButton({ - objectName: buttonName, - imageURL: Script.resolvePath("assets/images/tools/help.svg"), - visible: true, - hoverState: 2, - defaultState: 1, - buttonState: 1, - alpha: 0.9 - }); - - // TODO: make button state reflect whether the window is opened or closed (independently from us). - - function onClicked(){ - Menu.triggerOption('Help...') - } - - button.clicked.connect(onClicked); - - Script.scriptEnding.connect(function () { - toolBar.removeButton(buttonName); - button.clicked.disconnect(onClicked); - }); - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 26d37d15c7..0261b2f684 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -10,6 +10,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/*globals HMD, Toolbars, Script, Menu, Tablet, Camera */ (function() { // BEGIN LOCAL_SCOPE @@ -35,23 +36,37 @@ function updateControllerDisplay() { } } -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button; +var toolBar = null; +var tablet = null; + +if (HMD.hudUIEnabled) { + toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +} else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); +} + // Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through. // Disable them in hmd. var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode']; function onHmdChanged(isHmd) { - //TODO change button icon when the hmd changes - if (isHmd) { - button.editProperties({ - icon: "icons/tablet-icons/switch-a.svg", - text: "DESKTOP" - }); + if (HMD.hudUIEnabled) { + button.writeProperty('buttonState', isHmd ? 0 : 1); + button.writeProperty('defaultState', isHmd ? 0 : 1); + button.writeProperty('hoverState', isHmd ? 2 : 3); } else { - button.editProperties({ - icon: "icons/tablet-icons/switch-i.svg", - text: "VR" - }); + //TODO change button icon when the hmd changes + if (isHmd) { + button.editProperties({ + icon: "icons/tablet-icons/switch-a.svg", + text: "DESKTOP" + }); + } else { + button.editProperties({ + icon: "icons/tablet-icons/switch-i.svg", + text: "VR" + }); + } } desktopOnlyViews.forEach(function (view) { Menu.setMenuEnabled("View>" + view, !isHmd); @@ -63,10 +78,21 @@ function onClicked(){ Menu.setIsOptionChecked(isDesktop ? headset : desktopMenuItemName, true); } if (headset) { - button = tablet.addButton({ - icon: "icons/tablet-icons/switch-a.svg", - text: "SWITCH" - }); + if (HMD.hudUIEnabled) { + button = toolBar.addButton({ + objectName: "hmdToggle", + imageURL: Script.resolvePath("assets/images/tools/switch.svg"), + visible: true, + hoverState: 2, + defaultState: 0, + alpha: 0.9 + }); + } else { + button = tablet.addButton({ + icon: "icons/tablet-icons/switch-a.svg", + text: "SWITCH" + }); + } onHmdChanged(HMD.active); button.clicked.connect(onClicked); @@ -75,7 +101,12 @@ if (headset) { Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); - tablet.removeButton(button); + if (tablet) { + tablet.removeButton(button); + } + if (toolBar) { + toolBar.removeButton("hmdToggle"); + } HMD.displayModeChanged.disconnect(onHmdChanged); Camera.modeUpdated.disconnect(updateControllerDisplay); }); diff --git a/scripts/system/hmdHUD.js b/scripts/system/hmdHUD.js deleted file mode 100644 index 5dd06de8eb..0000000000 --- a/scripts/system/hmdHUD.js +++ /dev/null @@ -1,79 +0,0 @@ -"use strict"; - -// -// hmd.js -// scripts/system/ -// -// Created by Howard Stearns on 2 Jun 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - -var headset; // The preferred headset. Default to the first one found in the following list. -var displayMenuName = "Display"; -var desktopMenuItemName = "Desktop"; -['OpenVR (Vive)', 'Oculus Rift'].forEach(function (name) { - if (!headset && Menu.menuItemExists(displayMenuName, name)) { - headset = name; - } -}); - -var controllerDisplay = false; -function updateControllerDisplay() { - if (HMD.active && Menu.isOptionChecked("Third Person")) { - if (!controllerDisplay) { - HMD.requestShowHandControllers(); - controllerDisplay = true; - } - } else if (controllerDisplay) { - HMD.requestHideHandControllers(); - controllerDisplay = false; - } -} - -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); -var button; -// Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through. -// Disable them in hmd. -var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode']; -function onHmdChanged(isHmd) { - button.writeProperty('buttonState', isHmd ? 0 : 1); - button.writeProperty('defaultState', isHmd ? 0 : 1); - button.writeProperty('hoverState', isHmd ? 2 : 3); - desktopOnlyViews.forEach(function (view) { - Menu.setMenuEnabled("View>" + view, !isHmd); - }); - updateControllerDisplay(); -} -function onClicked(){ - var isDesktop = Menu.isOptionChecked(desktopMenuItemName); - Menu.setIsOptionChecked(isDesktop ? headset : desktopMenuItemName, true); -} -if (headset) { - button = toolBar.addButton({ - objectName: "hmdToggle", - imageURL: Script.resolvePath("assets/images/tools/switch.svg"), - visible: true, - hoverState: 2, - defaultState: 0, - alpha: 0.9 - }); - onHmdChanged(HMD.active); - - button.clicked.connect(onClicked); - HMD.displayModeChanged.connect(onHmdChanged); - Camera.modeUpdated.connect(updateControllerDisplay); - - Script.scriptEnding.connect(function () { - toolBar.removeButton("hmdToggle"); - button.clicked.disconnect(onClicked); - HMD.displayModeChanged.disconnect(onHmdChanged); - Camera.modeUpdated.disconnect(updateControllerDisplay); - }); -} - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/marketplaces/marketplaceHUD.js b/scripts/system/marketplaces/marketplaceHUD.js deleted file mode 100644 index 894dae7eac..0000000000 --- a/scripts/system/marketplaces/marketplaceHUD.js +++ /dev/null @@ -1,131 +0,0 @@ -// -// marketplace.js -// -// Created by Eric Levin on 8 Jan 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - -/* global WebTablet */ -Script.include("../libraries/WebTablet.js"); - -var toolIconUrl = Script.resolvePath("../assets/images/tools/"); - -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var marketplaceWindow = new OverlayWebWindow({ - title: "Marketplace", - source: "about:blank", - width: 900, - height: 700, - visible: false -}); - -var toolHeight = 50; -var toolWidth = 50; -var TOOLBAR_MARGIN_Y = 0; -var marketplaceVisible = false; -var marketplaceWebTablet; - -// We persist clientOnly data in the .ini file, and reconsistitute it on restart. -// To keep things consistent, we pickle the tablet data in Settings, and kill any existing such on restart and domain change. -var persistenceKey = "io.highfidelity.lastDomainTablet"; - -function shouldShowWebTablet() { - var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); - var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); - var hasHydra = !!Controller.Hardware.Hydra; - return HMD.active && (leftPose.valid || rightPose.valid || hasHydra); -} - -function showMarketplace(marketplaceID) { - if (shouldShowWebTablet()) { - updateButtonState(true); - marketplaceWebTablet = new WebTablet("https://metaverse.highfidelity.com/marketplace", null, null, true); - Settings.setValue(persistenceKey, marketplaceWebTablet.pickle()); - } else { - var url = MARKETPLACE_URL; - if (marketplaceID) { - url = url + "/items/" + marketplaceID; - } - marketplaceWindow.setURL(url); - marketplaceWindow.setVisible(true); - } - - marketplaceVisible = true; - UserActivityLogger.openedMarketplace(); -} - -function hideTablet(tablet) { - if (!tablet) { - return; - } - updateButtonState(false); - tablet.destroy(); - marketplaceWebTablet = null; - Settings.setValue(persistenceKey, ""); -} -function clearOldTablet() { // If there was a tablet from previous domain or session, kill it and let it be recreated - var tablet = WebTablet.unpickle(Settings.getValue(persistenceKey, "")); - hideTablet(tablet); -} -function hideMarketplace() { - if (marketplaceWindow.visible) { - marketplaceWindow.setVisible(false); - marketplaceWindow.setURL("about:blank"); - } else if (marketplaceWebTablet) { - hideTablet(marketplaceWebTablet); - } - marketplaceVisible = false; -} - -function toggleMarketplace() { - if (marketplaceVisible) { - hideMarketplace(); - } else { - showMarketplace(); - } -} - -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - -var browseExamplesButton = toolBar.addButton({ - imageURL: toolIconUrl + "market.svg", - objectName: "marketplace", - buttonState: 1, - defaultState: 1, - hoverState: 3, - alpha: 0.9 -}); - -function updateButtonState(visible) { - browseExamplesButton.writeProperty('buttonState', visible ? 0 : 1); - browseExamplesButton.writeProperty('defaultState', visible ? 0 : 1); - browseExamplesButton.writeProperty('hoverState', visible ? 2 : 3); -} -function onMarketplaceWindowVisibilityChanged() { - updateButtonState(marketplaceWindow.visible); - marketplaceVisible = marketplaceWindow.visible; -} - -function onClick() { - toggleMarketplace(); -} - -browseExamplesButton.clicked.connect(onClick); -marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged); - -clearOldTablet(); // Run once at startup, in case there's anything laying around from a crash. -// We could also optionally do something like Window.domainChanged.connect(function () {Script.setTimeout(clearOldTablet, 2000)}), -// but the HUD version stays around, so lets do the same. - -Script.scriptEnding.connect(function () { - toolBar.removeButton("marketplace"); - browseExamplesButton.clicked.disconnect(onClick); - marketplaceWindow.visibleChanged.disconnect(onMarketplaceWindowVisibilityChanged); -}); - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 3e06b05edc..2b17cf3b7e 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global WebTablet Tablet */ +/* global Tablet, Script, HMD, Toolbars, UserActivityLogger, Entities */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE @@ -31,6 +31,8 @@ var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; +var marketplaceWindow = null; + var CLARA_DOWNLOAD_TITLE = "Preparing Download"; var messageBox = null; var isDownloadBeingCancelled = false; @@ -51,49 +53,54 @@ function onMessageBoxClosed(id, button) { Window.messageBoxClosed.connect(onMessageBoxClosed); function showMarketplace() { - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - UserActivityLogger.openedMarketplace(); - tablet.webEventReceived.connect(function (message) { - if (message === GOTO_DIRECTORY) { - tablet.gotoWebScreen(MARKETPLACES_URL); - } + if (tablet) { + tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + tablet.webEventReceived.connect(function (message) { + if (message === GOTO_DIRECTORY) { + tablet.gotoWebScreen(MARKETPLACES_URL); + } - if (message === QUERY_CAN_WRITE_ASSETS) { - tablet.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); - } + if (message === QUERY_CAN_WRITE_ASSETS) { + tablet.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); + } - if (message === WARN_USER_NO_PERMISSIONS) { - Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); - } + if (message === WARN_USER_NO_PERMISSIONS) { + Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); + } - if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { - if (isDownloadBeingCancelled) { + if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { + if (isDownloadBeingCancelled) { + return; + } + + var text = message.slice(CLARA_IO_STATUS.length); + if (messageBox === null) { + messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } else { + Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } return; } - var text = message.slice(CLARA_IO_STATUS.length); - if (messageBox === null) { - messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); - } else { - Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { + if (messageBox !== null) { + Window.closeMessageBox(messageBox); + messageBox = null; + } + return; } - return; - } - if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { - if (messageBox !== null) { - Window.closeMessageBox(messageBox); - messageBox = null; + if (message === CLARA_IO_CANCELLED_DOWNLOAD) { + isDownloadBeingCancelled = false; } - return; - } - - if (message === CLARA_IO_CANCELLED_DOWNLOAD) { - isDownloadBeingCancelled = false; - } - }); + }); + } else { + marketplaceWindow.setURL(MARKETPLACE_URL_INITIAL); + marketplaceWindow.setVisible(true); + marketplaceVisible = true; + } } function toggleMarketplace() { @@ -102,12 +109,35 @@ function toggleMarketplace() { showMarketplace(); } -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - -var marketplaceButton = tablet.addButton({ - icon: "icons/tablet-icons/market-i.svg", - text: "MARKET" -}); +var tablet = null; +var toolBar = null; +var marketplaceButton = null; +if (HMD.hudUIEnabled) { + marketplaceWindow = new OverlayWebWindow({ + title: "Marketplace", + source: "about:blank", + width: 900, + height: 700, + visible: false + }); + marketplaceWindow.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); + toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + var toolIconUrl = Script.resolvePath("../assets/images/tools/"); + marketplaceButton = toolBar.addButton({ + imageURL: toolIconUrl + "market.svg", + objectName: "marketplace", + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 + }); +} else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + marketplaceButton = tablet.addButton({ + icon: "icons/tablet-icons/market-i.svg", + text: "MARKET" + }); +} function onCanWriteAssetsChanged() { var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); @@ -122,7 +152,12 @@ marketplaceButton.clicked.connect(onClick); Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); Script.scriptEnding.connect(function () { - tablet.removeButton(marketplaceButton); + if (toolBar) { + toolBar.removeButton("marketplace"); + } + if (tablet) { + tablet.removeButton(marketplaceButton); + } Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); }); diff --git a/scripts/system/marketplaces/marketplacesHUD.js b/scripts/system/marketplaces/marketplacesHUD.js deleted file mode 100644 index d5530e7db2..0000000000 --- a/scripts/system/marketplaces/marketplacesHUD.js +++ /dev/null @@ -1,233 +0,0 @@ -// -// marketplaces.js -// -// Created by Eric Levin on 8 Jan 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - -/* global WebTablet */ -Script.include("../libraries/WebTablet.js"); - -var toolIconUrl = Script.resolvePath("../assets/images/tools/"); - -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. -var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); -var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); - -// Event bridge messages. -var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; -var CLARA_IO_STATUS = "CLARA.IO STATUS"; -var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; -var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; -var GOTO_DIRECTORY = "GOTO_DIRECTORY"; -var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; -var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; -var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; - -var CLARA_DOWNLOAD_TITLE = "Preparing Download"; -var messageBox = null; -var isDownloadBeingCancelled = false; - -var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel -var NO_BUTTON = 0; // QMessageBox::NoButton - -var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; - -var marketplaceWindow = new OverlayWebWindow({ - title: "Marketplace", - source: "about:blank", - width: 900, - height: 700, - visible: false -}); -marketplaceWindow.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); - -function onWebEventReceived(message) { - if (message === GOTO_DIRECTORY) { - var url = MARKETPLACES_URL; - if (marketplaceWindow.visible) { - marketplaceWindow.setURL(url); - } - if (marketplaceWebTablet) { - marketplaceWebTablet.setURL(url); - } - return; - } - if (message === QUERY_CAN_WRITE_ASSETS) { - var canWriteAssets = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); - if (marketplaceWindow.visible) { - marketplaceWindow.emitScriptEvent(canWriteAssets); - } - if (marketplaceWebTablet) { - marketplaceWebTablet.getOverlayObject().emitScriptEvent(canWriteAssets); - } - return; - } - if (message === WARN_USER_NO_PERMISSIONS) { - Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); - return; - } - - if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { - if (isDownloadBeingCancelled) { - return; - } - - var text = message.slice(CLARA_IO_STATUS.length); - if (messageBox === null) { - messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); - } else { - Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); - } - return; - } - - if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { - if (messageBox !== null) { - Window.closeMessageBox(messageBox); - messageBox = null; - } - return; - } - - if (message === CLARA_IO_CANCELLED_DOWNLOAD) { - isDownloadBeingCancelled = false; - } -} - -marketplaceWindow.webEventReceived.connect(onWebEventReceived); - -function onMessageBoxClosed(id, button) { - if (id === messageBox && button === CANCEL_BUTTON) { - isDownloadBeingCancelled = true; - messageBox = null; - marketplaceWindow.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); - } -} - -Window.messageBoxClosed.connect(onMessageBoxClosed); - -var toolHeight = 50; -var toolWidth = 50; -var TOOLBAR_MARGIN_Y = 0; -var marketplaceVisible = false; -var marketplaceWebTablet; - -// We persist clientOnly data in the .ini file, and reconstitute it on restart. -// To keep things consistent, we pickle the tablet data in Settings, and kill any existing such on restart and domain change. -var persistenceKey = "io.highfidelity.lastDomainTablet"; - -function shouldShowWebTablet() { - var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); - var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); - var hasHydra = !!Controller.Hardware.Hydra; - return HMD.active && (leftPose.valid || rightPose.valid || hasHydra); -} - -function showMarketplace() { - if (shouldShowWebTablet()) { - updateButtonState(true); - marketplaceWebTablet = new WebTablet(MARKETPLACE_URL_INITIAL, null, null, true); - Settings.setValue(persistenceKey, marketplaceWebTablet.pickle()); - marketplaceWebTablet.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); - marketplaceWebTablet.getOverlayObject().webEventReceived.connect(onWebEventReceived); - } else { - marketplaceWindow.setURL(MARKETPLACE_URL_INITIAL); - marketplaceWindow.setVisible(true); - } - - marketplaceVisible = true; - UserActivityLogger.openedMarketplace(); -} - -function hideTablet(tablet) { - if (!tablet) { - return; - } - updateButtonState(false); - tablet.destroy(); - marketplaceWebTablet = null; - Settings.setValue(persistenceKey, ""); -} -function clearOldTablet() { // If there was a tablet from previous domain or session, kill it and let it be recreated - var tablet = WebTablet.unpickle(Settings.getValue(persistenceKey, "")); - hideTablet(tablet); -} -function hideMarketplace() { - if (marketplaceWindow.visible) { - marketplaceWindow.setVisible(false); - marketplaceWindow.setURL("about:blank"); - } else if (marketplaceWebTablet) { - hideTablet(marketplaceWebTablet); - } - marketplaceVisible = false; -} -marketplaceWindow.closed.connect(function () { - marketplaceWindow.setURL("about:blank"); -}); - -function toggleMarketplace() { - if (marketplaceVisible) { - hideMarketplace(); - } else { - showMarketplace(); - } -} - -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - -var browseExamplesButton = toolBar.addButton({ - imageURL: toolIconUrl + "market.svg", - objectName: "marketplace", - buttonState: 1, - defaultState: 1, - hoverState: 3, - alpha: 0.9 -}); - -function updateButtonState(visible) { - browseExamplesButton.writeProperty('buttonState', visible ? 0 : 1); - browseExamplesButton.writeProperty('defaultState', visible ? 0 : 1); - browseExamplesButton.writeProperty('hoverState', visible ? 2 : 3); -} -function onMarketplaceWindowVisibilityChanged() { - updateButtonState(marketplaceWindow.visible); - marketplaceVisible = marketplaceWindow.visible; -} - -function onCanWriteAssetsChanged() { - var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); - if (marketplaceWindow.visible) { - marketplaceWindow.emitScriptEvent(message); - } - if (marketplaceWebTablet) { - marketplaceWebTablet.getOverlayObject().emitScriptEvent(message); - } -} - -function onClick() { - toggleMarketplace(); -} - -browseExamplesButton.clicked.connect(onClick); -marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged); -Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); - -clearOldTablet(); // Run once at startup, in case there's anything laying around from a crash. -// We could also optionally do something like Window.domainChanged.connect(function () {Script.setTimeout(clearOldTablet, 2000)}), -// but the HUD version stays around, so lets do the same. - -Script.scriptEnding.connect(function () { - toolBar.removeButton("marketplace"); - browseExamplesButton.clicked.disconnect(onClick); - marketplaceWindow.visibleChanged.disconnect(onMarketplaceWindowVisibilityChanged); - Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); -}); - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/mute.js b/scripts/system/mute.js index 03e0d784f1..147e87d3e4 100644 --- a/scripts/system/mute.js +++ b/scripts/system/mute.js @@ -13,30 +13,53 @@ (function() { // BEGIN LOCAL_SCOPE -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - -var button = tablet.addButton({ - icon: "icons/tablet-icons/mic-a.svg", - text: "MUTE", - activeIcon: "icons/tablet-icons/mic-i.svg", - activeText: "UNMUTE" -}); +var button; +var buttonName = "MUTE"; +var toolBar = null; +var tablet = null; function onMuteToggled() { button.editProperties({isActive: AudioDevice.getMuted()}); } -onMuteToggled(); function onClicked(){ var menuItem = "Mute Microphone"; Menu.setIsOptionChecked(menuItem, !Menu.isOptionChecked(menuItem)); } + +if (HMD.hudUIEnabled) { + toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/mic.svg"), + visible: true, + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 + }); +} else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + button = tablet.addButton({ + icon: "icons/tablet-icons/mic-a.svg", + text: buttonName, + activeIcon: "icons/// TODO: ablet-icons/mic-i.svg", + activeText: "UNMUTE" + }); +} +onMuteToggled(); + button.clicked.connect(onClicked); AudioDevice.muteToggled.connect(onMuteToggled); Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); - tablet.removeButton(button); AudioDevice.muteToggled.disconnect(onMuteToggled); + if (tablet) { + tablet.removeButton(button); + } + if (toolBar) { + toolBar.removeButton(buttonName); + } }); }()); // END LOCAL_SCOPE diff --git a/scripts/system/muteHUD.js b/scripts/system/muteHUD.js deleted file mode 100644 index 722ed65b3d..0000000000 --- a/scripts/system/muteHUD.js +++ /dev/null @@ -1,51 +0,0 @@ -"use strict"; - -// -// goto.js -// scripts/system/ -// -// Created by Howard Stearns on 2 Jun 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - -var button = toolBar.addButton({ - objectName: "mute", - imageURL: Script.resolvePath("assets/images/tools/mic.svg"), - visible: true, - buttonState: 1, - defaultState: 1, - hoverState: 3, - alpha: 0.9 -}); - -function onMuteToggled() { - // We could just toggle state, but we're less likely to get out of wack if we read the AudioDevice. - // muted => button "on" state => 1. go figure. - var state = AudioDevice.getMuted() ? 0 : 1; - var hoverState = AudioDevice.getMuted() ? 2 : 3; - button.writeProperty('buttonState', state); - button.writeProperty('defaultState', state); - button.writeProperty('hoverState', hoverState); -} -onMuteToggled(); -function onClicked(){ - var menuItem = "Mute Microphone"; - Menu.setIsOptionChecked(menuItem, !Menu.isOptionChecked(menuItem)); -} -button.clicked.connect(onClicked); -AudioDevice.muteToggled.connect(onMuteToggled); - -Script.scriptEnding.connect(function () { - toolBar.removeButton("mute"); - button.clicked.disconnect(onClicked); - AudioDevice.muteToggled.disconnect(onMuteToggled); -}); - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 71d0182d08..7a5546c949 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -477,12 +477,28 @@ triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Cont // // Manage the connection between the button and the window. // -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); +var button; var buttonName = "PAL"; -var button = tablet.addButton({ - text: buttonName, - icon: "icons/tablet-icons/people-i.svg" -}); +var tablet = null; +var toolBar = null; +if (HMD.hudUIEnabled) { + toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/people.svg"), + visible: true, + hoverState: 2, + defaultState: 1, + buttonState: 1, + alpha: 0.9 + }); +} else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + button = tablet.addButton({ + text: buttonName, + icon: "icons/tablet-icons/people-i.svg" + }); +} var isWired = false; function off() { if (isWired) { // It is not ok to disconnect these twice, hence guard. @@ -623,7 +639,12 @@ Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); // Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); - tablet.removeButton(button); + if (tablet) { + tablet.removeButton(button); + } + if (toolBar) { + toolBar.removeButton(buttonName); + } pal.visibleChanged.disconnect(onVisibleChanged); pal.closed.disconnect(off); Users.usernameFromIDReply.disconnect(usernameFromIDReply); diff --git a/scripts/system/palHUD.js b/scripts/system/palHUD.js deleted file mode 100644 index f148ad5fdb..0000000000 --- a/scripts/system/palHUD.js +++ /dev/null @@ -1,658 +0,0 @@ -"use strict"; -/*jslint vars: true, plusplus: true, forin: true*/ -/*globals Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, OverlayWindow, Toolbars, Vec3, Quat, Controller, print, getControllerWorldLocation */ -// -// pal.js -// -// Created by Howard Stearns on December 9, 2016 -// Copyright 2016 High Fidelity, Inc -// -// Distributed under the Apache License, Version 2.0 -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed -// something, will revisit as this is sorta horrible. -const UNSELECTED_TEXTURES = {"idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png"), - "idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png") -}; -const SELECTED_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png"), - "idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png") -}; -const HOVER_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-hover.png"), - "idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-hover.png") -}; - -const UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6}; -const SELECTED_COLOR = {red: 0xF3, green: 0x91, blue: 0x29}; -const HOVER_COLOR = {red: 0xD0, green: 0xD0, blue: 0xD0}; // almost white for now - -(function() { // BEGIN LOCAL_SCOPE - -Script.include("/~/system/libraries/controllers.js"); - -// -// Overlays. -// -var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier. - -function ExtendedOverlay(key, type, properties, selected, hasModel) { // A wrapper around overlays to store the key it is associated with. - overlays[key] = this; - if (hasModel) { - var modelKey = key + "-m"; - this.model = new ExtendedOverlay(modelKey, "model", { - url: Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx"), - textures: textures(selected), - ignoreRayIntersection: true - }, false, false); - } else { - this.model = undefined; - } - this.key = key; - this.selected = selected || false; // not undefined - this.hovering = false; - this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected... -} -// Instance methods: -ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay - Overlays.deleteOverlay(this.activeOverlay); - delete overlays[this.key]; -}; - -ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay - Overlays.editOverlay(this.activeOverlay, properties); -}; - -function color(selected, hovering, level) { - var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; - function scale(component) { - var delta = 0xFF - component; - return component + (delta * level); - } - return {red: scale(base.red), green: scale(base.green), blue: scale(base.blue)}; -} - -function textures(selected, hovering) { - return hovering ? HOVER_TEXTURES : selected ? SELECTED_TEXTURES : UNSELECTED_TEXTURES; -} -// so we don't have to traverse the overlays to get the last one -var lastHoveringId = 0; -ExtendedOverlay.prototype.hover = function (hovering) { - this.hovering = hovering; - if (this.key === lastHoveringId) { - if (hovering) { - return; - } else { - lastHoveringId = 0; - } - } - this.editOverlay({color: color(this.selected, hovering, this.audioLevel)}); - if (this.model) { - this.model.editOverlay({textures: textures(this.selected, hovering)}); - } - if (hovering) { - // un-hover the last hovering overlay - if (lastHoveringId && lastHoveringId != this.key) { - ExtendedOverlay.get(lastHoveringId).hover(false); - } - lastHoveringId = this.key; - } -} -ExtendedOverlay.prototype.select = function (selected) { - if (this.selected === selected) { - return; - } - - UserActivityLogger.palAction(selected ? "avatar_selected" : "avatar_deselected", this.key); - - this.editOverlay({color: color(selected, this.hovering, this.audioLevel)}); - if (this.model) { - this.model.editOverlay({textures: textures(selected)}); - } - this.selected = selected; -}; -// Class methods: -var selectedIds = []; -ExtendedOverlay.isSelected = function (id) { - return -1 !== selectedIds.indexOf(id); -}; -ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier - return overlays[key]; -}; -ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy. - var key; - for (key in overlays) { - if (iterator(ExtendedOverlay.get(key))) { - return; - } - } -}; -ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any) - if (lastHoveringId) { - ExtendedOverlay.get(lastHoveringId).hover(false); - } -}; - -// hit(overlay) on the one overlay intersected by pickRay, if any. -// noHit() if no ExtendedOverlay was intersected (helps with hover) -ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { - var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. - if (!pickedOverlay.intersects) { - if (noHit) { - return noHit(); - } - return; - } - ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. - if ((overlay.activeOverlay) === pickedOverlay.overlayID) { - hit(overlay); - return true; - } - }); -}; - - -// -// Similar, for entities -// -function HighlightedEntity(id, entityProperties) { - this.id = id; - this.overlay = Overlays.addOverlay('cube', { - position: entityProperties.position, - rotation: entityProperties.rotation, - dimensions: entityProperties.dimensions, - solid: false, - color: { - red: 0xF3, - green: 0x91, - blue: 0x29 - }, - lineWidth: 1.0, - ignoreRayIntersection: true, - drawInFront: false // Arguable. For now, let's not distract with mysterious wires around the scene. - }); - HighlightedEntity.overlays.push(this); -} -HighlightedEntity.overlays = []; -HighlightedEntity.clearOverlays = function clearHighlightedEntities() { - HighlightedEntity.overlays.forEach(function (highlighted) { - Overlays.deleteOverlay(highlighted.overlay); - }); - HighlightedEntity.overlays = []; -}; -HighlightedEntity.updateOverlays = function updateHighlightedEntities() { - HighlightedEntity.overlays.forEach(function (highlighted) { - var properties = Entities.getEntityProperties(highlighted.id, ['position', 'rotation', 'dimensions']); - Overlays.editOverlay(highlighted.overlay, { - position: properties.position, - rotation: properties.rotation, - dimensions: properties.dimensions - }); - }); -}; - -// -// The qml window and communications. -// -var pal = new OverlayWindow({ - title: 'People Action List', - source: 'hifi/Pal.qml', - width: 580, - height: 640, - visible: false -}); -pal.fromQml.connect(function (message) { // messages are {method, params}, like json-rpc. See also sendToQml. - print('From PAL QML:', JSON.stringify(message)); - switch (message.method) { - case 'selected': - selectedIds = message.params; - ExtendedOverlay.some(function (overlay) { - var id = overlay.key; - var selected = ExtendedOverlay.isSelected(id); - overlay.select(selected); - }); - - HighlightedEntity.clearOverlays(); - if (selectedIds.length) { - Entities.findEntitiesInFrustum(Camera.frustum).forEach(function (id) { - // Because lastEditedBy is per session, the vast majority of entities won't match, - // so it would probably be worth reducing marshalling costs by asking for just we need. - // However, providing property name(s) is advisory and some additional properties are - // included anyway. As it turns out, asking for 'lastEditedBy' gives 'position', 'rotation', - // and 'dimensions', too, so we might as well make use of them instead of making a second - // getEntityProperties call. - // It would be nice if we could harden this against future changes by specifying all - // and only these four in an array, but see - // https://highfidelity.fogbugz.com/f/cases/2728/Entities-getEntityProperties-id-lastEditedBy-name-lastEditedBy-doesn-t-work - var properties = Entities.getEntityProperties(id, 'lastEditedBy'); - if (ExtendedOverlay.isSelected(properties.lastEditedBy)) { - new HighlightedEntity(id, properties); - } - }); - } - break; - case 'refresh': - removeOverlays(); - populateUserList(message.params); - UserActivityLogger.palAction("refresh", ""); - break; - case 'updateGain': - data = message.params; - if (data['isReleased']) { - // isReleased=true happens once at the end of a cycle of dragging - // the slider about, but with same gain as last isReleased=false so - // we don't set the gain in that case, and only here do we want to - // send an analytic event. - UserActivityLogger.palAction("avatar_gain_changed", data['sessionId']); - } else { - Users.setAvatarGain(data['sessionId'], data['gain']); - } - break; - case 'displayNameUpdate': - if (MyAvatar.displayName != message.params) { - MyAvatar.displayName = message.params; - UserActivityLogger.palAction("display_name_change", ""); - } - break; - default: - print('Unrecognized message from Pal.qml:', JSON.stringify(message)); - } -}); - -// -// Main operations. -// -function addAvatarNode(id) { - var selected = ExtendedOverlay.isSelected(id); - return new ExtendedOverlay(id, "sphere", { - drawInFront: true, - solid: true, - alpha: 0.8, - color: color(selected, false, 0.0), - ignoreRayIntersection: false}, selected, true); -} -function populateUserList(selectData) { - var data = []; - AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { // sorting the identifiers is just an aid for debugging - var avatar = AvatarList.getAvatar(id); - var avatarPalDatum = { - displayName: avatar.sessionDisplayName, - userName: '', - sessionId: id || '', - audioLevel: 0.0, - admin: false - }; - // Request the username, fingerprint, and admin status from the given UUID - // Username and fingerprint returns default constructor output if the requesting user isn't an admin - Users.requestUsernameFromID(id); - // Request personal mute status and ignore status - // from NodeList (as long as we're not requesting it for our own ID) - if (id) { - avatarPalDatum['personalMute'] = Users.getPersonalMuteStatus(id); - avatarPalDatum['ignore'] = Users.getIgnoreStatus(id); - addAvatarNode(id); // No overlay for ourselves - } - data.push(avatarPalDatum); - print('PAL data:', JSON.stringify(avatarPalDatum)); - }); - pal.sendToQml({ method: 'users', params: data }); - if (selectData) { - selectData[2] = true; - pal.sendToQml({ method: 'select', params: selectData }); - } -} - -// The function that handles the reply from the server -function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { - var data; - // If the ID we've received is our ID... - if (MyAvatar.sessionUUID === id) { - // Set the data to contain specific strings. - data = ['', username, isAdmin]; - } else if (Users.canKick) { - // Set the data to contain the ID and the username (if we have one) - // or fingerprint (if we don't have a username) string. - data = [id, username || machineFingerprint, isAdmin]; - } else { - // Set the data to contain specific strings. - data = [id, '', isAdmin]; - } - print('Username Data:', JSON.stringify(data)); - // Ship the data off to QML - pal.sendToQml({ method: 'updateUsername', params: data }); -} - -var pingPong = true; -function updateOverlays() { - var eye = Camera.position; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - if (!id) { - return; // don't update ourself - } - - var overlay = ExtendedOverlay.get(id); - if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. - print('Adding non-PAL avatar node', id); - overlay = addAvatarNode(id); - } - var avatar = AvatarList.getAvatar(id); - var target = avatar.position; - var distance = Vec3.distance(target, eye); - var offset = 0.2; - - // base offset on 1/2 distance from hips to head if we can - var headIndex = avatar.getJointIndex("Head"); - if (headIndex > 0) { - offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; - } - - // get diff between target and eye (a vector pointing to the eye from avatar position) - var diff = Vec3.subtract(target, eye); - - // move a bit in front, towards the camera - target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); - - // now bump it up a bit - target.y = target.y + offset; - - overlay.ping = pingPong; - overlay.editOverlay({ - color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel), - position: target, - dimensions: 0.032 * distance - }); - if (overlay.model) { - overlay.model.ping = pingPong; - overlay.model.editOverlay({ - position: target, - scale: 0.2 * distance, // constant apparent size - rotation: Camera.orientation - }); - } - }); - pingPong = !pingPong; - ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.) - if (overlay.ping === pingPong) { - overlay.deleteOverlay(); - } - }); - // We could re-populateUserList if anything added or removed, but not for now. - HighlightedEntity.updateOverlays(); -} -function removeOverlays() { - selectedIds = []; - lastHoveringId = 0; - HighlightedEntity.clearOverlays(); - ExtendedOverlay.some(function (overlay) { overlay.deleteOverlay(); }); -} - -// -// Clicks. -// -function handleClick(pickRay) { - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - // Don't select directly. Tell qml, who will give us back a list of ids. - var message = {method: 'select', params: [[overlay.key], !overlay.selected, false]}; - pal.sendToQml(message); - return true; - }); -} -function handleMouseEvent(mousePressEvent) { // handleClick if we get one. - if (!mousePressEvent.isLeftButton) { - return; - } - handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); -} -function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - overlay.hover(true); - }, function () { - ExtendedOverlay.unHover(); - }); -} - -// handy global to keep track of which hand is the mouse (if any) -var currentHandPressed = 0; -const TRIGGER_CLICK_THRESHOLD = 0.85; -const TRIGGER_PRESS_THRESHOLD = 0.05; - -function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position - if (HMD.active) { - if (currentHandPressed != 0) { - pickRay = controllerComputePickRay(currentHandPressed); - } else { - // nothing should hover, so - ExtendedOverlay.unHover(); - return; - } - } else { - pickRay = Camera.computePickRay(event.x, event.y); - } - handleMouseMove(pickRay); -} -function handleTriggerPressed(hand, value) { - // The idea is if you press one trigger, it is the one - // we will consider the mouse. Even if the other is pressed, - // we ignore it until this one is no longer pressed. - isPressed = value > TRIGGER_PRESS_THRESHOLD; - if (currentHandPressed == 0) { - currentHandPressed = isPressed ? hand : 0; - return; - } - if (currentHandPressed == hand) { - currentHandPressed = isPressed ? hand : 0; - return; - } - // otherwise, the other hand is still triggered - // so do nothing. -} - -// We get mouseMoveEvents from the handControllers, via handControllerPointer. -// But we don't get mousePressEvents. -var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); -var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); -function controllerComputePickRay(hand) { - var controllerPose = getControllerWorldLocation(hand, true); - if (controllerPose.valid) { - return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) }; - } -} -function makeClickHandler(hand) { - return function (clicked) { - if (clicked > TRIGGER_CLICK_THRESHOLD) { - var pickRay = controllerComputePickRay(hand); - handleClick(pickRay); - } - }; -} -function makePressHandler(hand) { - return function (value) { - handleTriggerPressed(hand, value); - } -} -triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); -triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); -triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); -triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); -// -// Message from other scripts, such as edit.js -// -var CHANNEL = 'com.highfidelity.pal'; -function receiveMessage(channel, messageString, senderID) { - if ((channel !== CHANNEL) || - (senderID !== MyAvatar.sessionUUID)) { - return; - } - var message = JSON.parse(messageString); - switch (message.method) { - case 'select': - if (!pal.visible) { - onClicked(); - } - pal.sendToQml(message); // Accepts objects, not just strings. - break; - default: - print('Unrecognized PAL message', messageString); - } -} -Messages.subscribe(CHANNEL); -Messages.messageReceived.connect(receiveMessage); - - -var AVERAGING_RATIO = 0.05; -var LOUDNESS_FLOOR = 11.0; -var LOUDNESS_SCALE = 2.8 / 5.0; -var LOG2 = Math.log(2.0); -var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) -var myData = {}; // we're not includied in ExtendedOverlay.get. -var audioInterval; - -function getAudioLevel(id) { - // the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged - // But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency - // of updating (the latter for efficiency too). - var avatar = AvatarList.getAvatar(id); - var audioLevel = 0.0; - var data = id ? ExtendedOverlay.get(id) : myData; - if (!data) { - return audioLevel; - } - - // we will do exponential moving average by taking some the last loudness and averaging - data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatar.audioLoudness); - - // add 1 to insure we don't go log() and hit -infinity. Math.log is - // natural log, so to get log base 2, just divide by ln(2). - var logLevel = Math.log(data.accumulatedLevel + 1) / LOG2; - - if (logLevel <= LOUDNESS_FLOOR) { - audioLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE; - } else { - audioLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE; - } - if (audioLevel > 1.0) { - audioLevel = 1; - } - data.audioLevel = audioLevel; - return audioLevel; -} - -function createAudioInterval() { - // we will update the audioLevels periodically - // TODO: tune for efficiency - expecially with large numbers of avatars - return Script.setInterval(function () { - if (pal.visible) { - var param = {}; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - var level = getAudioLevel(id); - // qml didn't like an object with null/empty string for a key, so... - var userId = id || 0; - param[userId] = level; - }); - pal.sendToQml({method: 'updateAudioLevel', params: param}); - } - }, AUDIO_LEVEL_UPDATE_INTERVAL_MS); -} - -// -// Manage the connection between the button and the window. -// -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); -var buttonName = "pal"; -var button = toolBar.addButton({ - objectName: buttonName, - imageURL: Script.resolvePath("assets/images/tools/people.svg"), - visible: true, - hoverState: 2, - defaultState: 1, - buttonState: 1, - alpha: 0.9 -}); - -var isWired = false; -var palOpenedAt; - -function off() { - if (isWired) { // It is not ok to disconnect these twice, hence guard. - Script.update.disconnect(updateOverlays); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - isWired = false; - } - triggerMapping.disable(); // It's ok if we disable twice. - triggerPressMapping.disable(); // see above - removeOverlays(); - Users.requestsDomainListData = false; - if (palOpenedAt) { - var duration = new Date().getTime() - palOpenedAt; - UserActivityLogger.palOpened(duration / 1000.0); - palOpenedAt = 0; // just a falsy number is good enough. - } - if (audioInterval) { - Script.clearInterval(audioInterval); - } -} -function onClicked() { - if (!pal.visible) { - Users.requestsDomainListData = true; - populateUserList(); - pal.raise(); - isWired = true; - Script.update.connect(updateOverlays); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - triggerMapping.enable(); - triggerPressMapping.enable(); - createAudioInterval(); - palOpenedAt = new Date().getTime(); - } else { - off(); - } - pal.setVisible(!pal.visible); -} -function avatarDisconnected(nodeID) { - // remove from the pal list - pal.sendToQml({method: 'avatarDisconnected', params: [nodeID]}); -} -// -// Button state. -// -function onVisibleChanged() { - button.writeProperty('buttonState', pal.visible ? 0 : 1); - button.writeProperty('defaultState', pal.visible ? 0 : 1); - button.writeProperty('hoverState', pal.visible ? 2 : 3); -} -button.clicked.connect(onClicked); -pal.visibleChanged.connect(onVisibleChanged); -pal.closed.connect(off); -Users.usernameFromIDReply.connect(usernameFromIDReply); -Users.avatarDisconnected.connect(avatarDisconnected); - -function clearLocalQMLDataAndClosePAL() { - pal.sendToQml({ method: 'clearLocalQMLData' }); - if (pal.visible) { - onClicked(); // Close the PAL - } -} -Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); -Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); - -// -// Cleanup. -// -Script.scriptEnding.connect(function () { - button.clicked.disconnect(onClicked); - toolBar.removeButton(buttonName); - pal.visibleChanged.disconnect(onVisibleChanged); - pal.closed.disconnect(off); - Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL); - Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL); - Messages.unsubscribe(CHANNEL); - Messages.messageReceived.disconnect(receiveMessage); - Users.avatarDisconnected.disconnect(avatarDisconnected); - off(); -}); - - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 1909061cb1..6d2fb01d29 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -7,19 +7,39 @@ // Distributed under the Apache License, Version 2.0 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* globals Tablet, Toolbars, Script, HMD, Settings, DialogsManager, Menu, Reticle, OverlayWebWindow, Desktop, Account, MyAvatar */ (function() { // BEGIN LOCAL_SCOPE var SNAPSHOT_DELAY = 500; // 500ms var FINISH_SOUND_DELAY = 350; -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var resetOverlays; var reticleVisible; var clearOverlayWhenMoving; -var button = tablet.addButton({ - icon: "icons/tablet-icons/snap-i.svg", - text: "SNAP" -}); + +var button; +var buttonName = "SNAP"; +var tablet = null; +var toolBar = null; + +if (HMD.hudUIEnabled) { + toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/snap.svg"), + visible: true, + buttonState: 1, + defaultState: 1, + hoverState: 2, + alpha: 0.9, + }); +} else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + button = tablet.addButton({ + icon: "icons/tablet-icons/snap-i.svg", + text: buttonName + }); +} function shouldOpenFeedAfterShare() { var persisted = Settings.getValue('openFeedAfterShare', true); // might answer true, false, "true", or "false" @@ -51,10 +71,10 @@ function confirmShare(data) { Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog"); break; case 'setOpenFeedFalse': - Settings.setValue('openFeedAfterShare', false) + Settings.setValue('openFeedAfterShare', false); break; case 'setOpenFeedTrue': - Settings.setValue('openFeedAfterShare', true) + Settings.setValue('openFeedAfterShare', true); break; default: dialog.webEventReceived.disconnect(onMessage); @@ -200,7 +220,12 @@ Window.processingGif.connect(processingGif); Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); - tablet.removeButton(button); + if (tablet) { + tablet.removeButton(button); + } + if (toolBar) { + toolBar.removeButton(buttonName); + } Window.snapshotShared.disconnect(snapshotShared); Window.processingGif.disconnect(processingGif); }); diff --git a/scripts/system/snapshotHUD.js b/scripts/system/snapshotHUD.js deleted file mode 100644 index d79a6e46cb..0000000000 --- a/scripts/system/snapshotHUD.js +++ /dev/null @@ -1,226 +0,0 @@ -// -// snapshot.js -// -// Created by David Kelly on 1 August 2016 -// Copyright 2016 High Fidelity, Inc -// -// Distributed under the Apache License, Version 2.0 -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - -var SNAPSHOT_DELAY = 500; // 500ms -var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); -var resetOverlays; -var reticleVisible; -var clearOverlayWhenMoving; -var button = toolBar.addButton({ - objectName: "snapshot", - imageURL: Script.resolvePath("assets/images/tools/snap.svg"), - visible: true, - buttonState: 1, - defaultState: 1, - hoverState: 2, - alpha: 0.9, -}); - -function shouldOpenFeedAfterShare() { - var persisted = Settings.getValue('openFeedAfterShare', true); // might answer true, false, "true", or "false" - return persisted && (persisted !== 'false'); -} -function showFeedWindow() { - DialogsManager.showFeed(); -} - -var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html"); - -var outstanding; -function confirmShare(data) { - var dialog = new OverlayWebWindow('Snapshot Review', SNAPSHOT_REVIEW_URL, 800, 520); - function onMessage(message) { - // Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following: - // 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.) - // 2. Although we currently use a single image, we would like to take snapshot, a selfie, a 360 etc. all at the - // same time, show the user all of them, and have the user deselect any that they do not want to share. - // So we'll ultimately be receiving a set of objects, perhaps with different post processing for each. - var isLoggedIn; - var needsLogin = false; - switch (message) { - case 'ready': - dialog.emitScriptEvent(data); // Send it. - outstanding = 0; - break; - case 'openSettings': - Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog"); - break; - case 'setOpenFeedFalse': - Settings.setValue('openFeedAfterShare', false) - break; - case 'setOpenFeedTrue': - Settings.setValue('openFeedAfterShare', true) - break; - default: - dialog.webEventReceived.disconnect(onMessage); - dialog.close(); - isLoggedIn = Account.isLoggedIn(); - message.forEach(function (submessage) { - if (submessage.share && !isLoggedIn) { - needsLogin = true; - submessage.share = false; - } - if (submessage.share) { - print('sharing', submessage.localPath); - outstanding++; - Window.shareSnapshot(submessage.localPath, submessage.href); - } else { - print('not sharing', submessage.localPath); - } - }); - if (!outstanding && shouldOpenFeedAfterShare()) { - showFeedWindow(); - } - if (needsLogin) { // after the possible feed, so that the login is on top - Account.checkAndSignalForAccessToken(); - } - } - } - dialog.webEventReceived.connect(onMessage); - dialog.raise(); -} - -function snapshotShared(errorMessage) { - if (!errorMessage) { - print('snapshot uploaded and shared'); - } else { - print(errorMessage); - } - if ((--outstanding <= 0) && shouldOpenFeedAfterShare()) { - showFeedWindow(); - } -} -var href, domainId; -function onClicked() { - // Raising the desktop for the share dialog at end will interact badly with clearOverlayWhenMoving. - // Turn it off now, before we start futzing with things (and possibly moving). - clearOverlayWhenMoving = MyAvatar.getClearOverlayWhenMoving(); // Do not use Settings. MyAvatar keeps a separate copy. - MyAvatar.setClearOverlayWhenMoving(false); - - // We will record snapshots based on the starting location. That could change, e.g., when recording a .gif. - // Even the domainId could change (e.g., if the user falls into a teleporter while recording). - href = location.href; - domainId = location.domainId; - - // update button states - resetOverlays = Menu.isOptionChecked("Overlays"); // For completness. Certainly true if the button is visible to be clicke. - reticleVisible = Reticle.visible; - Reticle.visible = false; - Window.snapshotTaken.connect(resetButtons); - - button.writeProperty("buttonState", 0); - button.writeProperty("defaultState", 0); - button.writeProperty("hoverState", 2); - - // hide overlays if they are on - if (resetOverlays) { - Menu.setIsOptionChecked("Overlays", false); - } - - // hide hud - toolBar.writeProperty("visible", false); - - // take snapshot (with no notification) - Script.setTimeout(function () { - Window.takeSnapshot(false, true, 1.91); - }, SNAPSHOT_DELAY); -} - -function isDomainOpen(id) { - var request = new XMLHttpRequest(); - var options = [ - 'now=' + new Date().toISOString(), - 'include_actions=concurrency', - 'domain_id=' + id.slice(1, -1), - 'restriction=open,hifi' // If we're sharing, we're logged in - // If we're here, protocol matches, and it is online - ]; - var url = location.metaverseServerUrl + "/api/v1/user_stories?" + options.join('&'); - request.open("GET", url, false); - request.send(); - if (request.status != 200) { - return false; - } - var response = JSON.parse(request.response); // Not parsed for us. - return (response.status === 'success') && - response.total_entries; -} - -function resetButtons(pathStillSnapshot, pathAnimatedSnapshot, notify) { - // If we're not taking an animated snapshot, we have to show the HUD. - // If we ARE taking an animated snapshot, we've already re-enabled the HUD by this point. - if (pathAnimatedSnapshot === "") { - // show hud - toolBar.writeProperty("visible", true); - Reticle.visible = reticleVisible; - // show overlays if they were on - if (resetOverlays) { - Menu.setIsOptionChecked("Overlays", true); - } - } else { - // Allow the user to click the snapshot HUD button again - button.clicked.connect(onClicked); - } - // update button states - button.writeProperty("buttonState", 1); - button.writeProperty("defaultState", 1); - button.writeProperty("hoverState", 3); - Window.snapshotTaken.disconnect(resetButtons); - - // A Snapshot Review dialog might be left open indefinitely after taking the picture, - // during which time the user may have moved. So stash that info in the dialog so that - // it records the correct href. (We can also stash in .jpegs, but not .gifs.) - // last element in data array tells dialog whether we can share or not - var confirmShareContents = [ - { localPath: pathStillSnapshot, href: href }, - { - canShare: !!isDomainOpen(domainId), - openFeedAfterShare: shouldOpenFeedAfterShare() - }]; - if (pathAnimatedSnapshot !== "") { - confirmShareContents.unshift({ localPath: pathAnimatedSnapshot, href: href }); - } - confirmShare(confirmShareContents); - if (clearOverlayWhenMoving) { - MyAvatar.setClearOverlayWhenMoving(true); // not until after the share dialog - } -} - -function processingGif() { - // show hud - toolBar.writeProperty("visible", true); - Reticle.visible = reticleVisible; - - // update button states - button.writeProperty("buttonState", 0); - button.writeProperty("defaultState", 0); - button.writeProperty("hoverState", 2); - // Don't allow the user to click the snapshot button yet - button.clicked.disconnect(onClicked); - // show overlays if they were on - if (resetOverlays) { - Menu.setIsOptionChecked("Overlays", true); - } -} - -button.clicked.connect(onClicked); -Window.snapshotShared.connect(snapshotShared); -Window.processingGif.connect(processingGif); - -Script.scriptEnding.connect(function () { - toolBar.removeButton("snapshot"); - button.clicked.disconnect(onClicked); - Window.snapshotShared.disconnect(snapshotShared); - Window.processingGif.disconnect(processingGif); -}); - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 017f9c0958..942534e3b6 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -108,6 +108,6 @@ Script.scriptEnding.connect(function () { Entities.deleteEntity(HMD.tabletID); HMD.tabletID = null; - HDM.homeButtonID = null; + HMD.homeButtonID = null; }); }()); // END LOCAL_SCOPE diff --git a/scripts/system/users.js b/scripts/system/users.js index 4c9663e203..b081768543 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -10,16 +10,37 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/*globals HMD, Toolbars, Script, Menu, Overlays, Tablet, Controller, Settings, OverlayWebWindow, Account, GlobalServices */ (function() { // BEGIN LOCAL_SCOPE +var button; +var buttonName = "USERS"; +var toolBar = null; +var tablet = null; + var MENU_ITEM = "Users Online"; -// create tablet button -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -var button = tablet.addButton({ - icon: "icons/tablet-icons/people-i.svg", - text: "Users", - isActive: Menu.isOptionChecked(MENU_ITEM) -}); + +if (HMD.hudUIEnabled) { + toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/people.svg"), + visible: true, + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 + }); +} else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + button = tablet.addButton({ + icon: "icons/tablet-icons/people-i.svg", + text: "Users", + isActive: Menu.isOptionChecked(MENU_ITEM) + }); +} + + function onClicked() { Menu.setIsOptionChecked(MENU_ITEM, !Menu.isOptionChecked(MENU_ITEM)); button.editProperties({isActive: Menu.isOptionChecked(MENU_ITEM)}); @@ -442,11 +463,11 @@ var usersWindow = (function () { } // Reserve space for title, friends button, and option controls - nonUsersHeight = WINDOW_MARGIN + windowLineHeight - + (shouldShowFriendsButton() ? FRIENDS_BUTTON_SPACER + FRIENDS_BUTTON_HEIGHT : 0) - + DISPLAY_SPACER - + windowLineHeight + VISIBILITY_SPACER - + windowLineHeight + WINDOW_BASE_MARGIN; + nonUsersHeight = WINDOW_MARGIN + windowLineHeight + + (shouldShowFriendsButton() ? FRIENDS_BUTTON_SPACER + FRIENDS_BUTTON_HEIGHT : 0) + + DISPLAY_SPACER + + windowLineHeight + VISIBILITY_SPACER + + windowLineHeight + WINDOW_BASE_MARGIN; // Limit window to height of viewport above window position minus VU meter and mirror if displayed windowHeight = linesOfUsers.length * windowLineHeight - windowLineSpacing + nonUsersHeight; @@ -504,8 +525,8 @@ var usersWindow = (function () { x: scrollbarBackgroundPosition.x, y: scrollbarBackgroundPosition.y }); - scrollbarBarPosition.y = scrollbarBackgroundPosition.y + 1 - + scrollbarValue * (scrollbarBackgroundHeight - scrollbarBarHeight - 2); + scrollbarBarPosition.y = scrollbarBackgroundPosition.y + 1 + + scrollbarValue * (scrollbarBackgroundHeight - scrollbarBarHeight - 2); Overlays.editOverlay(scrollbarBar, { x: scrollbarBackgroundPosition.x + 1, y: scrollbarBarPosition.y @@ -513,10 +534,10 @@ var usersWindow = (function () { x = windowLeft + WINDOW_MARGIN; - y = windowPosition.y - - DISPLAY_SPACER - - windowLineHeight - VISIBILITY_SPACER - - windowLineHeight - WINDOW_BASE_MARGIN; + y = windowPosition.y - + DISPLAY_SPACER - + windowLineHeight - VISIBILITY_SPACER - + windowLineHeight - WINDOW_BASE_MARGIN; if (shouldShowFriendsButton()) { y -= FRIENDS_BUTTON_HEIGHT; Overlays.editOverlay(friendsButton, { @@ -811,8 +832,8 @@ var usersWindow = (function () { userClicked = firstUserToDisplay + lineClicked; - if (0 <= userClicked && userClicked < linesOfUsers.length && 0 <= overlayX - && overlayX <= usersOnline[linesOfUsers[userClicked]].textWidth) { + if (0 <= userClicked && userClicked < linesOfUsers.length && 0 <= overlayX && + overlayX <= usersOnline[linesOfUsers[userClicked]].textWidth) { //print("Go to " + usersOnline[linesOfUsers[userClicked]].username); location.goToUser(usersOnline[linesOfUsers[userClicked]].username); } @@ -885,12 +906,12 @@ var usersWindow = (function () { } if (isMovingScrollbar) { - if (scrollbarBackgroundPosition.x - WINDOW_MARGIN <= event.x - && event.x <= scrollbarBackgroundPosition.x + SCROLLBAR_BACKGROUND_WIDTH + WINDOW_MARGIN - && scrollbarBackgroundPosition.y - WINDOW_MARGIN <= event.y - && event.y <= scrollbarBackgroundPosition.y + scrollbarBackgroundHeight + WINDOW_MARGIN) { - scrollbarValue = (event.y - scrollbarBarClickedAt * scrollbarBarHeight - scrollbarBackgroundPosition.y) - / (scrollbarBackgroundHeight - scrollbarBarHeight - 2); + if (scrollbarBackgroundPosition.x - WINDOW_MARGIN <= event.x && + event.x <= scrollbarBackgroundPosition.x + SCROLLBAR_BACKGROUND_WIDTH + WINDOW_MARGIN && + scrollbarBackgroundPosition.y - WINDOW_MARGIN <= event.y && + event.y <= scrollbarBackgroundPosition.y + scrollbarBackgroundHeight + WINDOW_MARGIN) { + scrollbarValue = (event.y - scrollbarBarClickedAt * scrollbarBarHeight - scrollbarBackgroundPosition.y) / + (scrollbarBackgroundHeight - scrollbarBarHeight - 2); scrollbarValue = Math.min(Math.max(scrollbarValue, 0.0), 1.0); firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); updateOverlayPositions(); @@ -918,13 +939,13 @@ var usersWindow = (function () { isVisible = isBorderVisible; if (isVisible) { - isVisible = windowPosition.x - WINDOW_BORDER_LEFT_MARGIN <= event.x - && event.x <= windowPosition.x - WINDOW_BORDER_LEFT_MARGIN + WINDOW_BORDER_WIDTH - && windowPosition.y - windowHeight - WINDOW_BORDER_TOP_MARGIN <= event.y - && event.y <= windowPosition.y + WINDOW_BORDER_BOTTOM_MARGIN; + isVisible = windowPosition.x - WINDOW_BORDER_LEFT_MARGIN <= event.x && + event.x <= windowPosition.x - WINDOW_BORDER_LEFT_MARGIN + WINDOW_BORDER_WIDTH && + windowPosition.y - windowHeight - WINDOW_BORDER_TOP_MARGIN <= event.y && + event.y <= windowPosition.y + WINDOW_BORDER_BOTTOM_MARGIN; } else { - isVisible = windowPosition.x <= event.x && event.x <= windowPosition.x + WINDOW_WIDTH - && windowPosition.y - windowHeight <= event.y && event.y <= windowPosition.y; + isVisible = windowPosition.x <= event.x && event.x <= windowPosition.x + WINDOW_WIDTH && + windowPosition.y - windowHeight <= event.y && event.y <= windowPosition.y; } if (isVisible !== isBorderVisible) { isBorderVisible = isVisible; @@ -951,10 +972,10 @@ var usersWindow = (function () { if (isMovingWindow) { // Save offset of bottom of window to nearest edge of the window. - offset.x = (windowPosition.x + WINDOW_WIDTH / 2 < viewport.x / 2) - ? windowPosition.x : windowPosition.x - viewport.x; - offset.y = (windowPosition.y < viewport.y / 2) - ? windowPosition.y : windowPosition.y - viewport.y; + offset.x = (windowPosition.x + WINDOW_WIDTH / 2 < viewport.x / 2) ? + windowPosition.x : windowPosition.x - viewport.x; + offset.y = (windowPosition.y < viewport.y / 2) ? + windowPosition.y : windowPosition.y - viewport.y; Settings.setValue(SETTING_USERS_WINDOW_OFFSET, JSON.stringify(offset)); isMovingWindow = false; } @@ -975,8 +996,8 @@ var usersWindow = (function () { isMirrorDisplay = Menu.isOptionChecked(MIRROR_MENU_ITEM); isFullscreenMirror = Menu.isOptionChecked(FULLSCREEN_MIRROR_MENU_ITEM); - if (viewport.y !== oldViewport.y || isMirrorDisplay !== oldIsMirrorDisplay - || isFullscreenMirror !== oldIsFullscreenMirror) { + if (viewport.y !== oldViewport.y || isMirrorDisplay !== oldIsMirrorDisplay || + isFullscreenMirror !== oldIsFullscreenMirror) { calculateWindowHeight(); updateUsersDisplay(); } @@ -1250,7 +1271,12 @@ var usersWindow = (function () { function cleanup () { //remove tablet button button.clicked.disconnect(onClicked); - tablet.removeButton(button); + if (tablet) { + tablet.removeButton(button); + } + if (toolBar) { + toolBar.removeButton(buttonName); + } } Script.scriptEnding.connect(cleanup); diff --git a/scripts/system/usersHUD.js b/scripts/system/usersHUD.js deleted file mode 100644 index 8c52240aa9..0000000000 --- a/scripts/system/usersHUD.js +++ /dev/null @@ -1,1237 +0,0 @@ -"use strict"; - -// -// users.js -// examples -// -// Created by David Rowe on 9 Mar 2015. -// 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 -// - -(function() { // BEGIN LOCAL_SCOPE - -// resolve these paths immediately -var MIN_MAX_BUTTON_SVG = Script.resolvePath("assets/images/tools/min-max-toggle.svg"); -var BASE_URL = Script.resolvePath("assets/images/tools/"); - -var PopUpMenu = function (properties) { - var value = properties.value, - promptOverlay, - valueOverlay, - buttonOverlay, - optionOverlays = [], - isDisplayingOptions = false, - OPTION_MARGIN = 4, - - MIN_MAX_BUTTON_SVG_WIDTH = 17.1, - MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, - MIN_MAX_BUTTON_WIDTH = 14, - MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH; - - function positionDisplayOptions() { - var y, - i; - - y = properties.y - (properties.values.length - 1) * properties.lineHeight - OPTION_MARGIN; - - for (i = 0; i < properties.values.length; i += 1) { - Overlays.editOverlay(optionOverlays[i], { - y: y - }); - y += properties.lineHeight; - } - } - - function showDisplayOptions() { - var i, - yOffScreen = Controller.getViewportDimensions().y; - - for (i = 0; i < properties.values.length; i += 1) { - optionOverlays[i] = Overlays.addOverlay("text", { - x: properties.x + properties.promptWidth, - y: yOffScreen, - width: properties.width - properties.promptWidth, - height: properties.textHeight + OPTION_MARGIN, // Only need to add margin at top to balance descenders - topMargin: OPTION_MARGIN, - leftMargin: OPTION_MARGIN, - color: properties.optionColor, - alpha: properties.optionAlpha, - backgroundColor: properties.popupBackgroundColor, - backgroundAlpha: properties.popupBackgroundAlpha, - text: properties.displayValues[i], - font: properties.font, - visible: true - }); - } - - positionDisplayOptions(); - - isDisplayingOptions = true; - } - - function deleteDisplayOptions() { - var i; - - for (i = 0; i < optionOverlays.length; i += 1) { - Overlays.deleteOverlay(optionOverlays[i]); - } - - isDisplayingOptions = false; - } - - function handleClick(overlay) { - var clicked = false, - i; - - if (overlay === valueOverlay || overlay === buttonOverlay) { - showDisplayOptions(); - return true; - } - - if (isDisplayingOptions) { - for (i = 0; i < optionOverlays.length; i += 1) { - if (overlay === optionOverlays[i]) { - value = properties.values[i]; - Overlays.editOverlay(valueOverlay, { - text: properties.displayValues[i] - }); - clicked = true; - } - } - - deleteDisplayOptions(); - } - - return clicked; - } - - function updatePosition(x, y) { - properties.x = x; - properties.y = y; - Overlays.editOverlay(promptOverlay, { - x: x, - y: y - }); - Overlays.editOverlay(valueOverlay, { - x: x + properties.promptWidth, - y: y - OPTION_MARGIN - }); - Overlays.editOverlay(buttonOverlay, { - x: x + properties.width - MIN_MAX_BUTTON_WIDTH - 1, - y: y - OPTION_MARGIN + 1 - }); - if (isDisplayingOptions) { - positionDisplayOptions(); - } - } - - function setVisible(visible) { - Overlays.editOverlay(promptOverlay, { - visible: visible - }); - Overlays.editOverlay(valueOverlay, { - visible: visible - }); - Overlays.editOverlay(buttonOverlay, { - visible: visible - }); - } - - function tearDown() { - Overlays.deleteOverlay(promptOverlay); - Overlays.deleteOverlay(valueOverlay); - Overlays.deleteOverlay(buttonOverlay); - if (isDisplayingOptions) { - deleteDisplayOptions(); - } - } - - function getValue() { - return value; - } - - function setValue(newValue) { - var index; - - index = properties.values.indexOf(newValue); - if (index !== -1) { - value = newValue; - Overlays.editOverlay(valueOverlay, { - text: properties.displayValues[index] - }); - } - } - - promptOverlay = Overlays.addOverlay("text", { - x: properties.x, - y: properties.y, - width: properties.promptWidth, - height: properties.textHeight, - topMargin: 0, - leftMargin: 0, - color: properties.promptColor, - alpha: properties.promptAlpha, - backgroundColor: properties.promptBackgroundColor, - backgroundAlpha: properties.promptBackgroundAlpha, - text: properties.prompt, - font: properties.font, - visible: properties.visible - }); - - valueOverlay = Overlays.addOverlay("text", { - x: properties.x + properties.promptWidth, - y: properties.y, - width: properties.width - properties.promptWidth, - height: properties.textHeight + OPTION_MARGIN, // Only need to add margin at top to balance descenders - topMargin: OPTION_MARGIN, - leftMargin: OPTION_MARGIN, - color: properties.optionColor, - alpha: properties.optionAlpha, - backgroundColor: properties.optionBackgroundColor, - backgroundAlpha: properties.optionBackgroundAlpha, - text: properties.displayValues[properties.values.indexOf(value)], - font: properties.font, - visible: properties.visible - }); - - buttonOverlay = Overlays.addOverlay("image", { - x: properties.x + properties.width - MIN_MAX_BUTTON_WIDTH - 1, - y: properties.y, - width: MIN_MAX_BUTTON_WIDTH, - height: MIN_MAX_BUTTON_HEIGHT, - imageURL: MIN_MAX_BUTTON_SVG, - subImage: { - x: 0, - y: 0, - width: MIN_MAX_BUTTON_SVG_WIDTH, - height: MIN_MAX_BUTTON_SVG_HEIGHT / 2 - }, - //color: properties.buttonColor, - alpha: properties.buttonAlpha, - visible: properties.visible - }); - - return { - updatePosition: updatePosition, - setVisible: setVisible, - handleClick: handleClick, - tearDown: tearDown, - getValue: getValue, - setValue: setValue - }; -}; - -var usersWindow = (function () { - - var WINDOW_WIDTH = 260, - WINDOW_MARGIN = 12, - WINDOW_BASE_MARGIN = 24, // A little less is needed in order look correct - WINDOW_FONT = { - size: 12 - }, - WINDOW_FOREGROUND_COLOR = { - red: 240, - green: 240, - blue: 240 - }, - WINDOW_FOREGROUND_ALPHA = 0.95, - WINDOW_HEADING_COLOR = { - red: 180, - green: 180, - blue: 180 - }, - WINDOW_HEADING_ALPHA = 0.95, - WINDOW_BACKGROUND_COLOR = { - red: 80, - green: 80, - blue: 80 - }, - WINDOW_BACKGROUND_ALPHA = 0.8, - windowPane, - windowHeading, - - // Margin on the left and right side of the window to keep - // it from getting too close to the edge of the screen which - // is unclickable. - WINDOW_MARGIN_X = 20, - - // Window border is similar to that of edit.js. - WINDOW_MARGIN_HALF = WINDOW_MARGIN / 2, - WINDOW_BORDER_WIDTH = WINDOW_WIDTH + 2 * WINDOW_MARGIN_HALF, - WINDOW_BORDER_TOP_MARGIN = 2 * WINDOW_MARGIN_HALF, - WINDOW_BORDER_BOTTOM_MARGIN = WINDOW_MARGIN_HALF, - WINDOW_BORDER_LEFT_MARGIN = WINDOW_MARGIN_HALF, - WINDOW_BORDER_RADIUS = 4, - WINDOW_BORDER_COLOR = { red: 255, green: 255, blue: 255 }, - WINDOW_BORDER_ALPHA = 0.5, - windowBorder, - - MIN_MAX_BUTTON_SVG = BASE_URL + "min-max-toggle.svg", - MIN_MAX_BUTTON_SVG_WIDTH = 17.1, - MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, - MIN_MAX_BUTTON_WIDTH = 14, - MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH, - MIN_MAX_BUTTON_COLOR = { - red: 255, - green: 255, - blue: 255 - }, - MIN_MAX_BUTTON_ALPHA = 0.9, - minimizeButton, - SCROLLBAR_BACKGROUND_WIDTH = 12, - SCROLLBAR_BACKGROUND_COLOR = { - red: 70, - green: 70, - blue: 70 - }, - SCROLLBAR_BACKGROUND_ALPHA = 0.8, - scrollbarBackground, - SCROLLBAR_BAR_MIN_HEIGHT = 5, - SCROLLBAR_BAR_COLOR = { - red: 170, - green: 170, - blue: 170 - }, - SCROLLBAR_BAR_ALPHA = 0.8, - SCROLLBAR_BAR_SELECTED_ALPHA = 0.95, - scrollbarBar, - scrollbarBackgroundHeight, - scrollbarBarHeight, - FRIENDS_BUTTON_SPACER = 6, // Space before add/remove friends button - FRIENDS_BUTTON_SVG = BASE_URL + "add-remove-friends.svg", - FRIENDS_BUTTON_SVG_WIDTH = 107, - FRIENDS_BUTTON_SVG_HEIGHT = 27, - FRIENDS_BUTTON_WIDTH = FRIENDS_BUTTON_SVG_WIDTH, - FRIENDS_BUTTON_HEIGHT = FRIENDS_BUTTON_SVG_HEIGHT, - FRIENDS_BUTTON_COLOR = { - red: 225, - green: 225, - blue: 225 - }, - FRIENDS_BUTTON_ALPHA = 0.95, - FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends", - FRIENDS_WINDOW_WIDTH = 290, - FRIENDS_WINDOW_HEIGHT = 500, - FRIENDS_WINDOW_TITLE = "Add/Remove Friends", - friendsButton, - friendsWindow, - - OPTION_BACKGROUND_COLOR = { - red: 60, - green: 60, - blue: 60 - }, - OPTION_BACKGROUND_ALPHA = 0.1, - - DISPLAY_SPACER = 12, // Space before display control - DISPLAY_PROMPT = "Show me:", - DISPLAY_PROMPT_WIDTH = 60, - DISPLAY_EVERYONE = "everyone", - DISPLAY_FRIENDS = "friends", - DISPLAY_VALUES = [DISPLAY_EVERYONE, DISPLAY_FRIENDS], - DISPLAY_DISPLAY_VALUES = DISPLAY_VALUES, - DISPLAY_OPTIONS_BACKGROUND_COLOR = { - red: 120, - green: 120, - blue: 120 - }, - DISPLAY_OPTIONS_BACKGROUND_ALPHA = 0.9, - displayControl, - - VISIBILITY_SPACER = 6, // Space before visibility control - VISIBILITY_PROMPT = "Visible to:", - VISIBILITY_PROMPT_WIDTH = 60, - VISIBILITY_ALL = "all", - VISIBILITY_FRIENDS = "friends", - VISIBILITY_NONE = "none", - VISIBILITY_VALUES = [VISIBILITY_ALL, VISIBILITY_FRIENDS, VISIBILITY_NONE], - VISIBILITY_DISPLAY_VALUES = ["everyone", "friends", "no one"], - visibilityControl, - - windowHeight, - windowBorderHeight, - windowTextHeight, - windowLineSpacing, - windowLineHeight, // = windowTextHeight + windowLineSpacing - windowMinimumHeight, - - usersOnline, // Raw users data - linesOfUsers = [], // Array of indexes pointing into usersOnline - numUsersToDisplay = 0, - firstUserToDisplay = 0, - - API_URL = "https://metaverse.highfidelity.com/api/v1/users?status=online", - API_FRIENDS_FILTER = "&filter=friends", - HTTP_GET_TIMEOUT = 60000, // ms = 1 minute - usersRequest, - processUsers, - pollUsersTimedOut, - usersTimer = null, - USERS_UPDATE_TIMEOUT = 5000, // ms = 5s - - showMe, - myVisibility, - - MENU_NAME = "View", - MENU_ITEM = "Users Online", - MENU_ITEM_OVERLAYS = "Overlays", - MENU_ITEM_AFTER = MENU_ITEM_OVERLAYS, - - SETTING_USERS_SHOW_ME = "UsersWindow.ShowMe", - SETTING_USERS_VISIBLE_TO = "UsersWindow.VisibleTo", - SETTING_USERS_WINDOW_MINIMIZED = "UsersWindow.Minimized", - SETTING_USERS_WINDOW_OFFSET = "UsersWindow.Offset", - // +ve x, y values are offset from left, top of screen; -ve from right, bottom. - - isLoggedIn = false, - isVisible = true, - isMinimized = false, - isBorderVisible = false, - - viewport, - isMirrorDisplay = false, - isFullscreenMirror = false, - - windowPosition = {}, // Bottom left corner of window pane. - isMovingWindow = false, - movingClickOffset = { x: 0, y: 0 }, - - isUsingScrollbars = false, - isMovingScrollbar = false, - scrollbarBackgroundPosition = {}, - scrollbarBarPosition = {}, - scrollbarBarClickedAt, // 0.0 .. 1.0 - scrollbarValue = 0.0; // 0.0 .. 1.0 - - function isWindowDisabled() { - return !Menu.isOptionChecked(MENU_ITEM) || !Menu.isOptionChecked(MENU_ITEM_OVERLAYS); - } - - function isValueTrue(value) { - // Work around Boolean Settings values being read as string when Interface starts up but as Booleans when re-read after - // Being written if refresh script. - return value === true || value === "true"; - } - - function calculateWindowHeight() { - var AUDIO_METER_HEIGHT = 52, - MIRROR_HEIGHT = 220, - nonUsersHeight, - maxWindowHeight; - - if (isMinimized) { - windowHeight = windowTextHeight + WINDOW_MARGIN + WINDOW_BASE_MARGIN; - windowBorderHeight = windowHeight + WINDOW_BORDER_TOP_MARGIN + WINDOW_BORDER_BOTTOM_MARGIN; - return; - } - - // Reserve space for title, friends button, and option controls - nonUsersHeight = WINDOW_MARGIN + windowLineHeight - + (shouldShowFriendsButton() ? FRIENDS_BUTTON_SPACER + FRIENDS_BUTTON_HEIGHT : 0) - + DISPLAY_SPACER - + windowLineHeight + VISIBILITY_SPACER - + windowLineHeight + WINDOW_BASE_MARGIN; - - // Limit window to height of viewport above window position minus VU meter and mirror if displayed - windowHeight = linesOfUsers.length * windowLineHeight - windowLineSpacing + nonUsersHeight; - maxWindowHeight = windowPosition.y - AUDIO_METER_HEIGHT; - if (isMirrorDisplay && !isFullscreenMirror) { - maxWindowHeight -= MIRROR_HEIGHT; - } - windowHeight = Math.max(Math.min(windowHeight, maxWindowHeight), nonUsersHeight); - windowBorderHeight = windowHeight + WINDOW_BORDER_TOP_MARGIN + WINDOW_BORDER_BOTTOM_MARGIN; - - // Corresponding number of users to actually display - numUsersToDisplay = Math.max(Math.round((windowHeight - nonUsersHeight) / windowLineHeight), 0); - isUsingScrollbars = 0 < numUsersToDisplay && numUsersToDisplay < linesOfUsers.length; - if (isUsingScrollbars) { - firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); - } else { - firstUserToDisplay = 0; - scrollbarValue = 0.0; - } - } - - function saturateWindowPosition() { - windowPosition.x = Math.max(WINDOW_MARGIN_X, Math.min(viewport.x - WINDOW_WIDTH - WINDOW_MARGIN_X, windowPosition.x)); - windowPosition.y = Math.max(windowMinimumHeight, Math.min(viewport.y, windowPosition.y)); - } - - function updateOverlayPositions() { - // Overlay positions are all relative to windowPosition; windowPosition is the position of the windowPane overlay. - var windowLeft = windowPosition.x, - windowTop = windowPosition.y - windowHeight, - x, - y; - - Overlays.editOverlay(windowBorder, { - x: windowPosition.x - WINDOW_BORDER_LEFT_MARGIN, - y: windowTop - WINDOW_BORDER_TOP_MARGIN - }); - Overlays.editOverlay(windowPane, { - x: windowLeft, - y: windowTop - }); - Overlays.editOverlay(windowHeading, { - x: windowLeft + WINDOW_MARGIN, - y: windowTop + WINDOW_MARGIN - }); - - Overlays.editOverlay(minimizeButton, { - x: windowLeft + WINDOW_WIDTH - WINDOW_MARGIN / 2 - MIN_MAX_BUTTON_WIDTH, - y: windowTop + WINDOW_MARGIN - }); - - scrollbarBackgroundPosition.x = windowLeft + WINDOW_WIDTH - 0.5 * WINDOW_MARGIN - SCROLLBAR_BACKGROUND_WIDTH; - scrollbarBackgroundPosition.y = windowTop + WINDOW_MARGIN + windowTextHeight; - Overlays.editOverlay(scrollbarBackground, { - x: scrollbarBackgroundPosition.x, - y: scrollbarBackgroundPosition.y - }); - scrollbarBarPosition.y = scrollbarBackgroundPosition.y + 1 - + scrollbarValue * (scrollbarBackgroundHeight - scrollbarBarHeight - 2); - Overlays.editOverlay(scrollbarBar, { - x: scrollbarBackgroundPosition.x + 1, - y: scrollbarBarPosition.y - }); - - - x = windowLeft + WINDOW_MARGIN; - y = windowPosition.y - - DISPLAY_SPACER - - windowLineHeight - VISIBILITY_SPACER - - windowLineHeight - WINDOW_BASE_MARGIN; - if (shouldShowFriendsButton()) { - y -= FRIENDS_BUTTON_HEIGHT; - Overlays.editOverlay(friendsButton, { - x: x, - y: y - }); - y += FRIENDS_BUTTON_HEIGHT; - } - - y += DISPLAY_SPACER; - displayControl.updatePosition(x, y); - - y += windowLineHeight + VISIBILITY_SPACER; - visibilityControl.updatePosition(x, y); - } - - function updateUsersDisplay() { - var displayText = "", - user, - userText, - textWidth, - maxTextWidth, - ellipsisWidth, - reducedTextWidth, - i; - - if (!isMinimized) { - maxTextWidth = WINDOW_WIDTH - (isUsingScrollbars ? SCROLLBAR_BACKGROUND_WIDTH : 0) - 2 * WINDOW_MARGIN; - ellipsisWidth = Overlays.textSize(windowPane, "...").width; - reducedTextWidth = maxTextWidth - ellipsisWidth; - - for (i = 0; i < numUsersToDisplay; i += 1) { - user = usersOnline[linesOfUsers[firstUserToDisplay + i]]; - userText = user.text; - textWidth = user.textWidth; - - if (textWidth > maxTextWidth) { - // Trim and append "..." to fit window width - maxTextWidth = maxTextWidth - Overlays.textSize(windowPane, "...").width; - while (textWidth > reducedTextWidth) { - userText = userText.slice(0, -1); - textWidth = Overlays.textSize(windowPane, userText).width; - } - userText += "..."; - } - - displayText += "\n" + userText; - } - - displayText = displayText.slice(1); // Remove leading "\n". - - scrollbarBackgroundHeight = numUsersToDisplay * windowLineHeight - windowLineSpacing / 2; - Overlays.editOverlay(scrollbarBackground, { - height: scrollbarBackgroundHeight, - visible: isLoggedIn && isUsingScrollbars - }); - scrollbarBarHeight = Math.max(numUsersToDisplay / linesOfUsers.length * scrollbarBackgroundHeight, - SCROLLBAR_BAR_MIN_HEIGHT); - Overlays.editOverlay(scrollbarBar, { - height: scrollbarBarHeight, - visible: isLoggedIn && isUsingScrollbars - }); - } - - Overlays.editOverlay(windowBorder, { - height: windowBorderHeight - }); - - Overlays.editOverlay(windowPane, { - height: windowHeight, - text: displayText - }); - - Overlays.editOverlay(windowHeading, { - text: isLoggedIn ? (linesOfUsers.length > 0 ? "Users online" : "No users online") : "Users online - log in to view" - }); - } - - function shouldShowFriendsButton() { - return isVisible && isLoggedIn && !isMinimized; - } - - function updateOverlayVisibility() { - Overlays.editOverlay(windowBorder, { - visible: isVisible && isBorderVisible - }); - Overlays.editOverlay(windowPane, { - visible: isVisible - }); - Overlays.editOverlay(windowHeading, { - visible: isVisible - }); - Overlays.editOverlay(minimizeButton, { - visible: isVisible - }); - Overlays.editOverlay(scrollbarBackground, { - visible: isVisible && isUsingScrollbars && !isMinimized - }); - Overlays.editOverlay(scrollbarBar, { - visible: isVisible && isUsingScrollbars && !isMinimized - }); - Overlays.editOverlay(friendsButton, { - visible: shouldShowFriendsButton() - }); - displayControl.setVisible(isVisible && !isMinimized); - visibilityControl.setVisible(isVisible && !isMinimized); - } - - function checkLoggedIn() { - var wasLoggedIn = isLoggedIn; - - isLoggedIn = Account.isLoggedIn(); - if (isLoggedIn !== wasLoggedIn) { - if (wasLoggedIn) { - setMinimized(true); - calculateWindowHeight(); - updateOverlayPositions(); - updateUsersDisplay(); - } - - updateOverlayVisibility(); - } - } - - function pollUsers() { - var url = API_URL; - - if (showMe === DISPLAY_FRIENDS) { - url += API_FRIENDS_FILTER; - } - - usersRequest = new XMLHttpRequest(); - usersRequest.open("GET", url, true); - usersRequest.timeout = HTTP_GET_TIMEOUT; - usersRequest.ontimeout = pollUsersTimedOut; - usersRequest.onreadystatechange = processUsers; - usersRequest.send(); - } - - processUsers = function () { - var response, - myUsername, - user, - userText, - i; - - if (usersRequest.readyState === usersRequest.DONE) { - if (usersRequest.status === 200) { - response = JSON.parse(usersRequest.responseText); - if (response.status !== "success") { - print("Error: Request for users status returned status = " + response.status); - usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. - return; - } - if (!response.hasOwnProperty("data") || !response.data.hasOwnProperty("users")) { - print("Error: Request for users status returned invalid data"); - usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. - return; - } - - usersOnline = response.data.users; - myUsername = GlobalServices.username; - linesOfUsers = []; - for (i = 0; i < usersOnline.length; i += 1) { - user = usersOnline[i]; - if (user.username !== myUsername && user.online) { - userText = user.username; - if (user.location.root) { - userText += " @ " + user.location.root.name; - } - - usersOnline[i].text = userText; - usersOnline[i].textWidth = Overlays.textSize(windowPane, userText).width; - - linesOfUsers.push(i); - } - } - - checkLoggedIn(); - calculateWindowHeight(); - updateUsersDisplay(); - updateOverlayPositions(); - - } else { - print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText); - usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. - return; - } - - usersTimer = Script.setTimeout(pollUsers, USERS_UPDATE_TIMEOUT); // Update after finished processing. - } - }; - - pollUsersTimedOut = function () { - print("Error: Request for users status timed out"); - usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. - }; - - function setVisible(visible) { - isVisible = visible; - - if (isVisible) { - if (usersTimer === null) { - pollUsers(); - } - } else { - Script.clearTimeout(usersTimer); - usersTimer = null; - } - - updateOverlayVisibility(); - } - - function setMinimized(minimized) { - isMinimized = minimized; - Overlays.editOverlay(minimizeButton, { - subImage: { - y: isMinimized ? MIN_MAX_BUTTON_SVG_HEIGHT / 2 : 0 - } - }); - updateOverlayVisibility(); - Settings.setValue(SETTING_USERS_WINDOW_MINIMIZED, isMinimized); - } - - function onMenuItemEvent(event) { - if (event === MENU_ITEM) { - setVisible(Menu.isOptionChecked(MENU_ITEM)); - } - } - - function onFindableByChanged(event) { - if (VISIBILITY_VALUES.indexOf(event) !== -1) { - myVisibility = event; - visibilityControl.setValue(event); - Settings.setValue(SETTING_USERS_VISIBLE_TO, myVisibility); - } else { - print("Error: Unrecognized onFindableByChanged value: " + event); - } - } - - function onMousePressEvent(event) { - var clickedOverlay, - numLinesBefore, - overlayX, - overlayY, - minY, - maxY, - lineClicked, - userClicked, - delta; - - if (!isVisible || isWindowDisabled()) { - return; - } - - clickedOverlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - - if (displayControl.handleClick(clickedOverlay)) { - if (usersTimer !== null) { - Script.clearTimeout(usersTimer); - usersTimer = null; - } - pollUsers(); - showMe = displayControl.getValue(); - Settings.setValue(SETTING_USERS_SHOW_ME, showMe); - return; - } - - if (visibilityControl.handleClick(clickedOverlay)) { - myVisibility = visibilityControl.getValue(); - GlobalServices.findableBy = myVisibility; - Settings.setValue(SETTING_USERS_VISIBLE_TO, myVisibility); - return; - } - - if (clickedOverlay === windowPane) { - - overlayX = event.x - windowPosition.x - WINDOW_MARGIN; - overlayY = event.y - windowPosition.y + windowHeight - WINDOW_MARGIN - windowLineHeight; - - numLinesBefore = Math.round(overlayY / windowLineHeight); - minY = numLinesBefore * windowLineHeight; - maxY = minY + windowTextHeight; - - lineClicked = -1; - if (minY <= overlayY && overlayY <= maxY) { - lineClicked = numLinesBefore; - } - - userClicked = firstUserToDisplay + lineClicked; - - if (0 <= userClicked && userClicked < linesOfUsers.length && 0 <= overlayX - && overlayX <= usersOnline[linesOfUsers[userClicked]].textWidth) { - //print("Go to " + usersOnline[linesOfUsers[userClicked]].username); - location.goToUser(usersOnline[linesOfUsers[userClicked]].username); - } - - return; - } - - if (clickedOverlay === minimizeButton) { - setMinimized(!isMinimized); - calculateWindowHeight(); - updateOverlayPositions(); - updateUsersDisplay(); - return; - } - - if (clickedOverlay === scrollbarBar) { - scrollbarBarClickedAt = (event.y - scrollbarBarPosition.y) / scrollbarBarHeight; - Overlays.editOverlay(scrollbarBar, { - backgroundAlpha: SCROLLBAR_BAR_SELECTED_ALPHA - }); - isMovingScrollbar = true; - return; - } - - if (clickedOverlay === scrollbarBackground) { - delta = scrollbarBarHeight / (scrollbarBackgroundHeight - scrollbarBarHeight); - - if (event.y < scrollbarBarPosition.y) { - scrollbarValue = Math.max(scrollbarValue - delta, 0.0); - } else { - scrollbarValue = Math.min(scrollbarValue + delta, 1.0); - } - - firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); - updateOverlayPositions(); - updateUsersDisplay(); - return; - } - - if (clickedOverlay === friendsButton) { - if (!friendsWindow) { - 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(); - return; - } - - if (clickedOverlay === windowBorder) { - movingClickOffset = { - x: event.x - windowPosition.x, - y: event.y - windowPosition.y - }; - - isMovingWindow = true; - } - } - - function onMouseMoveEvent(event) { - var isVisible; - - if (!isLoggedIn || isWindowDisabled()) { - return; - } - - if (isMovingScrollbar) { - if (scrollbarBackgroundPosition.x - WINDOW_MARGIN <= event.x - && event.x <= scrollbarBackgroundPosition.x + SCROLLBAR_BACKGROUND_WIDTH + WINDOW_MARGIN - && scrollbarBackgroundPosition.y - WINDOW_MARGIN <= event.y - && event.y <= scrollbarBackgroundPosition.y + scrollbarBackgroundHeight + WINDOW_MARGIN) { - scrollbarValue = (event.y - scrollbarBarClickedAt * scrollbarBarHeight - scrollbarBackgroundPosition.y) - / (scrollbarBackgroundHeight - scrollbarBarHeight - 2); - scrollbarValue = Math.min(Math.max(scrollbarValue, 0.0), 1.0); - firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); - updateOverlayPositions(); - updateUsersDisplay(); - } else { - Overlays.editOverlay(scrollbarBar, { - backgroundAlpha: SCROLLBAR_BAR_ALPHA - }); - isMovingScrollbar = false; - } - } - - if (isMovingWindow) { - windowPosition = { - x: event.x - movingClickOffset.x, - y: event.y - movingClickOffset.y - }; - - saturateWindowPosition(); - calculateWindowHeight(); - updateOverlayPositions(); - updateUsersDisplay(); - - } else { - - isVisible = isBorderVisible; - if (isVisible) { - isVisible = windowPosition.x - WINDOW_BORDER_LEFT_MARGIN <= event.x - && event.x <= windowPosition.x - WINDOW_BORDER_LEFT_MARGIN + WINDOW_BORDER_WIDTH - && windowPosition.y - windowHeight - WINDOW_BORDER_TOP_MARGIN <= event.y - && event.y <= windowPosition.y + WINDOW_BORDER_BOTTOM_MARGIN; - } else { - isVisible = windowPosition.x <= event.x && event.x <= windowPosition.x + WINDOW_WIDTH - && windowPosition.y - windowHeight <= event.y && event.y <= windowPosition.y; - } - if (isVisible !== isBorderVisible) { - isBorderVisible = isVisible; - Overlays.editOverlay(windowBorder, { - visible: isBorderVisible - }); - } - } - } - - function onMouseReleaseEvent() { - var offset = {}; - - if (isWindowDisabled()) { - return; - } - - if (isMovingScrollbar) { - Overlays.editOverlay(scrollbarBar, { - backgroundAlpha: SCROLLBAR_BAR_ALPHA - }); - isMovingScrollbar = false; - } - - if (isMovingWindow) { - // Save offset of bottom of window to nearest edge of the window. - offset.x = (windowPosition.x + WINDOW_WIDTH / 2 < viewport.x / 2) - ? windowPosition.x : windowPosition.x - viewport.x; - offset.y = (windowPosition.y < viewport.y / 2) - ? windowPosition.y : windowPosition.y - viewport.y; - Settings.setValue(SETTING_USERS_WINDOW_OFFSET, JSON.stringify(offset)); - isMovingWindow = false; - } - } - - function onScriptUpdate() { - var oldViewport = viewport, - oldIsMirrorDisplay = isMirrorDisplay, - oldIsFullscreenMirror = isFullscreenMirror, - MIRROR_MENU_ITEM = "Mirror", - FULLSCREEN_MIRROR_MENU_ITEM = "Fullscreen Mirror"; - - if (isWindowDisabled()) { - return; - } - - viewport = Controller.getViewportDimensions(); - isMirrorDisplay = Menu.isOptionChecked(MIRROR_MENU_ITEM); - isFullscreenMirror = Menu.isOptionChecked(FULLSCREEN_MIRROR_MENU_ITEM); - - if (viewport.y !== oldViewport.y || isMirrorDisplay !== oldIsMirrorDisplay - || isFullscreenMirror !== oldIsFullscreenMirror) { - calculateWindowHeight(); - updateUsersDisplay(); - } - - if (viewport.y !== oldViewport.y) { - if (windowPosition.y > oldViewport.y / 2) { - // Maintain position w.r.t. bottom of window. - windowPosition.y = viewport.y - (oldViewport.y - windowPosition.y); - } - } - - if (viewport.x !== oldViewport.x) { - if (windowPosition.x + (WINDOW_WIDTH / 2) > oldViewport.x / 2) { - // Maintain position w.r.t. right of window. - windowPosition.x = viewport.x - (oldViewport.x - windowPosition.x); - } - } - - updateOverlayPositions(); - } - - function setUp() { - var textSizeOverlay, - offsetSetting, - offset = {}, - hmdViewport; - - textSizeOverlay = Overlays.addOverlay("text", { - font: WINDOW_FONT, - visible: false - }); - windowTextHeight = Math.floor(Overlays.textSize(textSizeOverlay, "1").height); - windowLineSpacing = Math.floor(Overlays.textSize(textSizeOverlay, "1\n2").height - 2 * windowTextHeight); - windowLineHeight = windowTextHeight + windowLineSpacing; - windowMinimumHeight = windowTextHeight + WINDOW_MARGIN + WINDOW_BASE_MARGIN; - Overlays.deleteOverlay(textSizeOverlay); - - viewport = Controller.getViewportDimensions(); - - offsetSetting = Settings.getValue(SETTING_USERS_WINDOW_OFFSET); - if (offsetSetting !== "") { - offset = JSON.parse(Settings.getValue(SETTING_USERS_WINDOW_OFFSET)); - } - if (offset.hasOwnProperty("x") && offset.hasOwnProperty("y")) { - windowPosition.x = offset.x < 0 ? viewport.x + offset.x : offset.x; - windowPosition.y = offset.y <= 0 ? viewport.y + offset.y : offset.y; - } else { - hmdViewport = Controller.getRecommendedOverlayRect(); - windowPosition = { - x: (viewport.x - hmdViewport.width) / 2, // HMD viewport is narrower than screen. - y: hmdViewport.height // HMD viewport starts at top of screen but only extends down so far. - }; - } - - saturateWindowPosition(); - calculateWindowHeight(); - - windowBorder = Overlays.addOverlay("rectangle", { - x: 0, - y: viewport.y, // Start up off-screen - width: WINDOW_BORDER_WIDTH, - height: windowBorderHeight, - radius: WINDOW_BORDER_RADIUS, - color: WINDOW_BORDER_COLOR, - alpha: WINDOW_BORDER_ALPHA, - visible: false - }); - - windowPane = Overlays.addOverlay("text", { - x: 0, - y: viewport.y, - width: WINDOW_WIDTH, - height: windowHeight, - topMargin: WINDOW_MARGIN + windowLineHeight, - leftMargin: WINDOW_MARGIN, - color: WINDOW_FOREGROUND_COLOR, - alpha: WINDOW_FOREGROUND_ALPHA, - backgroundColor: WINDOW_BACKGROUND_COLOR, - backgroundAlpha: WINDOW_BACKGROUND_ALPHA, - text: "", - font: WINDOW_FONT, - visible: false - }); - - windowHeading = Overlays.addOverlay("text", { - x: 0, - y: viewport.y, - width: WINDOW_WIDTH - 2 * WINDOW_MARGIN, - height: windowTextHeight, - topMargin: 0, - leftMargin: 0, - color: WINDOW_HEADING_COLOR, - alpha: WINDOW_HEADING_ALPHA, - backgroundAlpha: 0.0, - text: "Users online", - font: WINDOW_FONT, - visible: false - }); - - minimizeButton = Overlays.addOverlay("image", { - x: 0, - y: viewport.y, - width: MIN_MAX_BUTTON_WIDTH, - height: MIN_MAX_BUTTON_HEIGHT, - imageURL: MIN_MAX_BUTTON_SVG, - subImage: { - x: 0, - y: 0, - width: MIN_MAX_BUTTON_SVG_WIDTH, - height: MIN_MAX_BUTTON_SVG_HEIGHT / 2 - }, - color: MIN_MAX_BUTTON_COLOR, - alpha: MIN_MAX_BUTTON_ALPHA, - visible: false - }); - - scrollbarBackgroundPosition = { - x: 0, - y: viewport.y - }; - scrollbarBackground = Overlays.addOverlay("text", { - x: 0, - y: scrollbarBackgroundPosition.y, - width: SCROLLBAR_BACKGROUND_WIDTH, - height: windowTextHeight, - backgroundColor: SCROLLBAR_BACKGROUND_COLOR, - backgroundAlpha: SCROLLBAR_BACKGROUND_ALPHA, - text: "", - visible: false - }); - - scrollbarBarPosition = { - x: 0, - y: viewport.y - }; - scrollbarBar = Overlays.addOverlay("text", { - x: 0, - y: scrollbarBarPosition.y, - width: SCROLLBAR_BACKGROUND_WIDTH - 2, - height: windowTextHeight, - backgroundColor: SCROLLBAR_BAR_COLOR, - backgroundAlpha: SCROLLBAR_BAR_ALPHA, - text: "", - visible: false - }); - - friendsButton = Overlays.addOverlay("image", { - x: 0, - y: viewport.y, - width: FRIENDS_BUTTON_WIDTH, - height: FRIENDS_BUTTON_HEIGHT, - imageURL: FRIENDS_BUTTON_SVG, - subImage: { - x: 0, - y: 0, - width: FRIENDS_BUTTON_SVG_WIDTH, - height: FRIENDS_BUTTON_SVG_HEIGHT - }, - color: FRIENDS_BUTTON_COLOR, - alpha: FRIENDS_BUTTON_ALPHA, - visible: false - }); - - showMe = Settings.getValue(SETTING_USERS_SHOW_ME, ""); - if (DISPLAY_VALUES.indexOf(showMe) === -1) { - showMe = DISPLAY_EVERYONE; - } - - displayControl = new PopUpMenu({ - prompt: DISPLAY_PROMPT, - value: showMe, - values: DISPLAY_VALUES, - displayValues: DISPLAY_DISPLAY_VALUES, - x: 0, - y: viewport.y, - width: WINDOW_WIDTH - 1.5 * WINDOW_MARGIN, - promptWidth: DISPLAY_PROMPT_WIDTH, - lineHeight: windowLineHeight, - textHeight: windowTextHeight, - font: WINDOW_FONT, - promptColor: WINDOW_HEADING_COLOR, - promptAlpha: WINDOW_HEADING_ALPHA, - promptBackgroundColor: WINDOW_BACKGROUND_COLOR, - promptBackgroundAlpha: 0.0, - optionColor: WINDOW_FOREGROUND_COLOR, - optionAlpha: WINDOW_FOREGROUND_ALPHA, - optionBackgroundColor: OPTION_BACKGROUND_COLOR, - optionBackgroundAlpha: OPTION_BACKGROUND_ALPHA, - popupBackgroundColor: DISPLAY_OPTIONS_BACKGROUND_COLOR, - popupBackgroundAlpha: DISPLAY_OPTIONS_BACKGROUND_ALPHA, - buttonColor: MIN_MAX_BUTTON_COLOR, - buttonAlpha: MIN_MAX_BUTTON_ALPHA, - visible: false - }); - - myVisibility = Settings.getValue(SETTING_USERS_VISIBLE_TO, ""); - if (VISIBILITY_VALUES.indexOf(myVisibility) === -1) { - myVisibility = VISIBILITY_FRIENDS; - } - GlobalServices.findableBy = myVisibility; - - visibilityControl = new PopUpMenu({ - prompt: VISIBILITY_PROMPT, - value: myVisibility, - values: VISIBILITY_VALUES, - displayValues: VISIBILITY_DISPLAY_VALUES, - x: 0, - y: viewport.y, - width: WINDOW_WIDTH - 1.5 * WINDOW_MARGIN, - promptWidth: VISIBILITY_PROMPT_WIDTH, - lineHeight: windowLineHeight, - textHeight: windowTextHeight, - font: WINDOW_FONT, - promptColor: WINDOW_HEADING_COLOR, - promptAlpha: WINDOW_HEADING_ALPHA, - promptBackgroundColor: WINDOW_BACKGROUND_COLOR, - promptBackgroundAlpha: 0.0, - optionColor: WINDOW_FOREGROUND_COLOR, - optionAlpha: WINDOW_FOREGROUND_ALPHA, - optionBackgroundColor: OPTION_BACKGROUND_COLOR, - optionBackgroundAlpha: OPTION_BACKGROUND_ALPHA, - popupBackgroundColor: DISPLAY_OPTIONS_BACKGROUND_COLOR, - popupBackgroundAlpha: DISPLAY_OPTIONS_BACKGROUND_ALPHA, - buttonColor: MIN_MAX_BUTTON_COLOR, - buttonAlpha: MIN_MAX_BUTTON_ALPHA, - visible: false - }); - - Controller.mousePressEvent.connect(onMousePressEvent); - Controller.mouseMoveEvent.connect(onMouseMoveEvent); - Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); - - Menu.addMenuItem({ - menuName: MENU_NAME, - menuItemName: MENU_ITEM, - afterItem: MENU_ITEM_AFTER, - isCheckable: true, - isChecked: isVisible - }); - Menu.menuItemEvent.connect(onMenuItemEvent); - - GlobalServices.findableByChanged.connect(onFindableByChanged); - - Script.update.connect(onScriptUpdate); - - pollUsers(); - - // Set minimized at end - setup code does not handle `minimized == false` correctly - setMinimized(isValueTrue(Settings.getValue(SETTING_USERS_WINDOW_MINIMIZED, true))); - } - - function tearDown() { - Menu.removeMenuItem(MENU_NAME, MENU_ITEM); - - Script.clearTimeout(usersTimer); - Overlays.deleteOverlay(windowBorder); - Overlays.deleteOverlay(windowPane); - Overlays.deleteOverlay(windowHeading); - Overlays.deleteOverlay(minimizeButton); - Overlays.deleteOverlay(scrollbarBackground); - Overlays.deleteOverlay(scrollbarBar); - Overlays.deleteOverlay(friendsButton); - displayControl.tearDown(); - visibilityControl.tearDown(); - } - - setUp(); - Script.scriptEnding.connect(tearDown); -}()); - -}()); // END LOCAL_SCOPE From 98f1664a46669666bcbf413f11cc6020276bcb37 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 25 Jan 2017 14:17:45 -0800 Subject: [PATCH 3/4] use a setting to decide which UI is enabled --- interface/src/scripting/HMDScriptingInterface.h | 7 +------ scripts/defaultScripts.js | 2 -- scripts/system/bubble.js | 2 +- scripts/system/edit.js | 2 +- scripts/system/goto.js | 2 +- scripts/system/help.js | 2 +- scripts/system/hmd.js | 6 +++--- scripts/system/marketplaces/marketplaces.js | 2 +- scripts/system/mute.js | 2 +- scripts/system/pal.js | 2 +- scripts/system/snapshot.js | 2 +- scripts/system/users.js | 2 +- 12 files changed, 13 insertions(+), 20 deletions(-) diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index a8c1600a16..f5744bb8d1 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -31,7 +31,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen Q_PROPERTY(bool showTablet READ getShouldShowTablet) Q_PROPERTY(QUuid tabletID READ getCurrentTableUIID WRITE setCurrentTabletUIID) Q_PROPERTY(unsigned int homeButtonID READ getCurrentHomeButtonUUID WRITE setCurrentHomeButtonUUID) - Q_PROPERTY(bool hudUIEnabled READ getHUDUIEnabled WRITE setHUDUIEnabled); + public: Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const; @@ -96,9 +96,6 @@ public: void setCurrentHomeButtonUUID(unsigned int homeButtonID) { _homeButtonID = homeButtonID; } unsigned int getCurrentHomeButtonUUID() const { return _homeButtonID; } - bool getHUDUIEnabled() const { return _hudUIEnabled; } - void setHUDUIEnabled(bool value) { _hudUIEnabled = value; } - private: bool _showTablet { false }; QUuid _tabletUIID; // this is the entityID of the WebEntity which is part of (a child of) the tablet-ui. @@ -114,8 +111,6 @@ private: bool getHUDLookAtPosition3D(glm::vec3& result) const; glm::mat4 getWorldHMDMatrix() const; std::atomic _showHandControllersCount { 0 }; - - bool _hudUIEnabled; }; #endif // hifi_HMDScriptingInterface_h diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index f046351147..bd3131f4ff 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -43,8 +43,6 @@ var DEFAULT_SCRIPTS = [ var MENU_CATEGORY = "Developer"; var MENU_ITEM = "Debug defaultScripts.js"; -HMD.hudUIEnabled = true; - var SETTINGS_KEY = '_debugDefaultScriptsIsChecked'; var previousSetting = Settings.getValue(SETTINGS_KEY); diff --git a/scripts/system/bubble.js b/scripts/system/bubble.js index 4d93926abb..b5134e096b 100644 --- a/scripts/system/bubble.js +++ b/scripts/system/bubble.js @@ -165,7 +165,7 @@ // Setup the bubble button var buttonName = "BUBBLE"; - if (HMD.hudUIEnabled) { + if (Settings.getValue("HUDUIEnabled")) { var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); button = toolbar.addButton({ objectName: 'bubble', diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 075bc4360d..7a6f063077 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -237,7 +237,7 @@ var toolBar = (function () { }); - if (HMD.hudUIEnabled) { + if (Settings.getValue("HUDUIEnabled")) { systemToolbar = Toolbars.getToolbar(SYSTEM_TOOLBAR); activeButton = systemToolbar.addButton({ objectName: EDIT_TOGGLE_BUTTON, diff --git a/scripts/system/goto.js b/scripts/system/goto.js index b9ae33339b..30f6ca255c 100644 --- a/scripts/system/goto.js +++ b/scripts/system/goto.js @@ -27,7 +27,7 @@ function onClicked(){ DialogsManager.toggleAddressBar(); } -if (HMD.hudUIEnabled) { +if (Settings.getValue("HUDUIEnabled")) { toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); button = toolBar.addButton({ objectName: buttonName, diff --git a/scripts/system/help.js b/scripts/system/help.js index 483a1c3dc4..f3f3007b65 100644 --- a/scripts/system/help.js +++ b/scripts/system/help.js @@ -18,7 +18,7 @@ var buttonName = "HELP"; var toolBar = null; var tablet = null; - if (HMD.hudUIEnabled) { + if (Settings.getValue("HUDUIEnabled")) { toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); button = toolBar.addButton({ objectName: buttonName, diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 0261b2f684..bd525d7b14 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -40,7 +40,7 @@ var button; var toolBar = null; var tablet = null; -if (HMD.hudUIEnabled) { +if (Settings.getValue("HUDUIEnabled")) { toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); } else { tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); @@ -50,7 +50,7 @@ if (HMD.hudUIEnabled) { // Disable them in hmd. var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode']; function onHmdChanged(isHmd) { - if (HMD.hudUIEnabled) { + if (Settings.getValue("HUDUIEnabled")) { button.writeProperty('buttonState', isHmd ? 0 : 1); button.writeProperty('defaultState', isHmd ? 0 : 1); button.writeProperty('hoverState', isHmd ? 2 : 3); @@ -78,7 +78,7 @@ function onClicked(){ Menu.setIsOptionChecked(isDesktop ? headset : desktopMenuItemName, true); } if (headset) { - if (HMD.hudUIEnabled) { + if (Settings.getValue("HUDUIEnabled")) { button = toolBar.addButton({ objectName: "hmdToggle", imageURL: Script.resolvePath("assets/images/tools/switch.svg"), diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 2b17cf3b7e..24eda5b6a3 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -112,7 +112,7 @@ function toggleMarketplace() { var tablet = null; var toolBar = null; var marketplaceButton = null; -if (HMD.hudUIEnabled) { +if (Settings.getValue("HUDUIEnabled")) { marketplaceWindow = new OverlayWebWindow({ title: "Marketplace", source: "about:blank", diff --git a/scripts/system/mute.js b/scripts/system/mute.js index 147e87d3e4..f94dbc242c 100644 --- a/scripts/system/mute.js +++ b/scripts/system/mute.js @@ -26,7 +26,7 @@ function onClicked(){ Menu.setIsOptionChecked(menuItem, !Menu.isOptionChecked(menuItem)); } -if (HMD.hudUIEnabled) { +if (Settings.getValue("HUDUIEnabled")) { toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); button = toolBar.addButton({ objectName: buttonName, diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7a5546c949..92bc410404 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -481,7 +481,7 @@ var button; var buttonName = "PAL"; var tablet = null; var toolBar = null; -if (HMD.hudUIEnabled) { +if (Settings.getValue("HUDUIEnabled")) { toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); button = toolBar.addButton({ objectName: buttonName, diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 6d2fb01d29..db0c7bd36e 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -22,7 +22,7 @@ var buttonName = "SNAP"; var tablet = null; var toolBar = null; -if (HMD.hudUIEnabled) { +if (Settings.getValue("HUDUIEnabled")) { toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); button = toolBar.addButton({ objectName: buttonName, diff --git a/scripts/system/users.js b/scripts/system/users.js index b081768543..ce795b38e5 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -20,7 +20,7 @@ var tablet = null; var MENU_ITEM = "Users Online"; -if (HMD.hudUIEnabled) { +if (Settings.getValue("HUDUIEnabled")) { toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); button = toolBar.addButton({ objectName: buttonName, From ff8d13ecc72889f9c56aed92a5fcf1b59f58ff6f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 25 Jan 2017 15:38:54 -0800 Subject: [PATCH 4/4] get button-states for toolbar buttons working again --- .../qml/hifi/tablet/TabletButton.qml | 2 - .../qml/hifi/toolbars/StateImage.qml | 2 +- .../qml/hifi/toolbars/ToolbarButton.qml | 37 +++++++++++++++---- .../scripting/ToolbarScriptingInterface.cpp | 20 +++++++++- scripts/system/edit.js | 2 - scripts/system/goto.js | 3 -- scripts/system/help.js | 3 -- scripts/system/hmd.js | 2 - scripts/system/marketplaces/marketplaces.js | 3 -- scripts/system/mute.js | 3 -- scripts/system/pal.js | 3 -- scripts/system/snapshot.js | 3 -- 12 files changed, 49 insertions(+), 34 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 6c2685f155..9ad8d1476c 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -125,7 +125,6 @@ Item { } } onEntered: { - console.log("Tablet Button Hovered!"); tabletButton.isEntered = true; if (tabletButton.isActive) { tabletButton.state = "hover active state"; @@ -134,7 +133,6 @@ Item { } } onExited: { - console.log("Tablet Button Unhovered!"); tabletButton.isEntered = false; if (tabletButton.isActive) { tabletButton.state = "active state"; diff --git a/interface/resources/qml/hifi/toolbars/StateImage.qml b/interface/resources/qml/hifi/toolbars/StateImage.qml index 44eaa6f7fd..ee0778626d 100644 --- a/interface/resources/qml/hifi/toolbars/StateImage.qml +++ b/interface/resources/qml/hifi/toolbars/StateImage.qml @@ -6,7 +6,7 @@ Item { property alias alpha: image.opacity property var subImage; property int yOffset: 0 - property int buttonState: 0 + property int buttonState: 1 property real size: 50 width: size; height: size property bool pinned: false diff --git a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml index bc035ca19c..91c992bf0d 100644 --- a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml +++ b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml @@ -3,11 +3,34 @@ import QtQuick.Controls 1.4 StateImage { id: button - property int hoverState: -1 - property int defaultState: -1 + property bool isActive: false + property bool isEntered: false + + property int imageOffOut: 1 + property int imageOffIn: 3 + property int imageOnOut: 0 + property int imageOnIn: 2 signal clicked() + function changeProperty(key, value) { + button[key] = value; + } + + function updateState() { + if (!button.isEntered && !button.isActive) { + buttonState = imageOffOut; + } else if (!button.isEntered && button.isActive) { + buttonState = imageOnOut; + } else if (button.isEntered && !button.isActive) { + buttonState = imageOffIn; + } else { + buttonState = imageOnIn; + } + } + + onIsActiveChanged: updateState(); + Timer { id: asyncClickSender interval: 10 @@ -22,14 +45,12 @@ StateImage { anchors.fill: parent onClicked: asyncClickSender.start(); onEntered: { - if (hoverState >= 0) { - buttonState = hoverState; - } + button.isEntered = true; + updateState(); } onExited: { - if (defaultState >= 0) { - buttonState = defaultState; - } + button.isEntered = false; + updateState(); } } } diff --git a/interface/src/scripting/ToolbarScriptingInterface.cpp b/interface/src/scripting/ToolbarScriptingInterface.cpp index eb60ff0f67..2b4f64f35e 100644 --- a/interface/src/scripting/ToolbarScriptingInterface.cpp +++ b/interface/src/scripting/ToolbarScriptingInterface.cpp @@ -19,15 +19,33 @@ class ToolbarButtonProxy : public QmlWrapper { public: ToolbarButtonProxy(QObject* qmlObject, QObject* parent = nullptr) : QmlWrapper(qmlObject, parent) { + std::lock_guard guard(_mutex); + _qmlButton = qobject_cast(qmlObject); connect(qmlObject, SIGNAL(clicked()), this, SIGNAL(clicked())); } Q_INVOKABLE void editProperties(QVariantMap properties) { - qDebug() << "XXX WRITE TabletButtonProxy::editProperties"; + std::lock_guard guard(_mutex); + QVariantMap::const_iterator iter = properties.constBegin(); + while (iter != properties.constEnd()) { + _properties[iter.key()] = iter.value(); + if (_qmlButton) { + // [01/25 14:26:20] [WARNING] [default] QMetaObject::invokeMethod: No such method ToolbarButton_QMLTYPE_195::changeProperty(QVariant,QVariant) + + QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection, + Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value())); + } + ++iter; + } } signals: void clicked(); + +protected: + mutable std::mutex _mutex; + QQuickItem* _qmlButton { nullptr }; + QVariantMap _properties; }; class ToolbarProxy : public QmlWrapper { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 7a6f063077..7d5c1fed1a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -244,8 +244,6 @@ var toolBar = (function () { imageURL: TOOLS_PATH + "edit.svg", visible: true, alpha: 0.9, - buttonState: 1, - hoverState: 3, defaultState: 1 }); } else { diff --git a/scripts/system/goto.js b/scripts/system/goto.js index 30f6ca255c..95bd05ae73 100644 --- a/scripts/system/goto.js +++ b/scripts/system/goto.js @@ -33,9 +33,6 @@ if (Settings.getValue("HUDUIEnabled")) { objectName: buttonName, imageURL: Script.resolvePath("assets/images/tools/directory.svg"), visible: true, - buttonState: 1, - defaultState: 1, - hoverState: 3, alpha: 0.9 }); } else { diff --git a/scripts/system/help.js b/scripts/system/help.js index f3f3007b65..7813780da3 100644 --- a/scripts/system/help.js +++ b/scripts/system/help.js @@ -24,9 +24,6 @@ objectName: buttonName, imageURL: Script.resolvePath("assets/images/tools/help.svg"), visible: true, - hoverState: 2, - defaultState: 1, - buttonState: 1, alpha: 0.9 }); } else { diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index bd525d7b14..794e348fd3 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -83,8 +83,6 @@ if (headset) { objectName: "hmdToggle", imageURL: Script.resolvePath("assets/images/tools/switch.svg"), visible: true, - hoverState: 2, - defaultState: 0, alpha: 0.9 }); } else { diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 24eda5b6a3..8cb13cf27e 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -126,9 +126,6 @@ if (Settings.getValue("HUDUIEnabled")) { marketplaceButton = toolBar.addButton({ imageURL: toolIconUrl + "market.svg", objectName: "marketplace", - buttonState: 1, - defaultState: 1, - hoverState: 3, alpha: 0.9 }); } else { diff --git a/scripts/system/mute.js b/scripts/system/mute.js index f94dbc242c..3aec12ff59 100644 --- a/scripts/system/mute.js +++ b/scripts/system/mute.js @@ -32,9 +32,6 @@ if (Settings.getValue("HUDUIEnabled")) { objectName: buttonName, imageURL: Script.resolvePath("assets/images/tools/mic.svg"), visible: true, - buttonState: 1, - defaultState: 1, - hoverState: 3, alpha: 0.9 }); } else { diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 92bc410404..a9d6d8b2e2 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -487,9 +487,6 @@ if (Settings.getValue("HUDUIEnabled")) { objectName: buttonName, imageURL: Script.resolvePath("assets/images/tools/people.svg"), visible: true, - hoverState: 2, - defaultState: 1, - buttonState: 1, alpha: 0.9 }); } else { diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index db0c7bd36e..fa2f2df7d3 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -28,9 +28,6 @@ if (Settings.getValue("HUDUIEnabled")) { objectName: buttonName, imageURL: Script.resolvePath("assets/images/tools/snap.svg"), visible: true, - buttonState: 1, - defaultState: 1, - hoverState: 2, alpha: 0.9, }); } else {