make use of tablet or HUD depend on a single variable. avoid some duplicated code

This commit is contained in:
Seth Alves 2017-01-25 12:00:34 -08:00
parent 11d01a0c00
commit f18094f565
25 changed files with 406 additions and 3188 deletions

View file

@ -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<int> _showHandControllersCount { 0 };
bool _hudUIEnabled;
};
#endif // hifi_HMDScriptingInterface_h

View file

@ -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();
};

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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();

View file

@ -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);
});

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
});

View file

@ -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

View file

@ -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

View file

@ -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);
});

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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);
});

View file

@ -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

View file

@ -108,6 +108,6 @@
Script.scriptEnding.connect(function () {
Entities.deleteEntity(HMD.tabletID);
HMD.tabletID = null;
HDM.homeButtonID = null;
HMD.homeButtonID = null;
});
}()); // END LOCAL_SCOPE

View file

@ -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);

File diff suppressed because it is too large Load diff