Refactor of pal.js to dynamically switch between toolbar and tablet.

This commit is contained in:
Anthony J. Thibault 2017-02-10 15:06:55 -08:00
parent d0dc642cd8
commit f8055a027e

View file

@ -1,6 +1,7 @@
"use strict";
/*jslint vars: true, plusplus: true, forin: true*/
/*globals Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, OverlayWindow, Toolbars, Vec3, Quat, Controller, print, getControllerWorldLocation */
/* jslint vars: true, plusplus: true, forin: true*/
/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, OverlayWindow, Toolbars, Vec3, Quat, Controller, print, getControllerWorldLocation */
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
//
// pal.js
//
@ -13,21 +14,24 @@
(function() { // BEGIN LOCAL_SCOPE
// hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed
// 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")
var 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")
var 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")
var 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
var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6};
var SELECTED_COLOR = {red: 0xF3, green: 0x91, blue: 0x29};
var HOVER_COLOR = {red: 0xD0, green: 0xD0, blue: 0xD0}; // almost white for now
var conserveResources = true;
@ -87,24 +91,24 @@ ExtendedOverlay.prototype.hover = function (hovering) {
} 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) {
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)});
@ -204,6 +208,7 @@ var pal = new OverlayWindow({
visible: false
});
function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
var data;
switch (message.method) {
case 'selected':
selectedIds = message.params;
@ -250,7 +255,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
}
break;
case 'displayNameUpdate':
if (MyAvatar.displayName != message.params) {
if (MyAvatar.displayName !== message.params) {
MyAvatar.displayName = message.params;
UserActivityLogger.palAction("display_name_change", "");
}
@ -261,9 +266,9 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
}
function sendToQml(message) {
if (Settings.getValue("HUDUIEnabled")) {
if (currentUIMode === "toolbar") {
pal.sendToQml(message);
} else {
} else if (currentUIMode === "tablet") {
tablet.sendToQml(message);
}
}
@ -273,7 +278,7 @@ function sendToQml(message) {
//
function addAvatarNode(id) {
var selected = ExtendedOverlay.isSelected(id);
return new ExtendedOverlay(id, "sphere", {
return new ExtendedOverlay(id, "sphere", {
drawInFront: true,
solid: true,
alpha: 0.8,
@ -339,7 +344,7 @@ function updateOverlays() {
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);
@ -349,7 +354,7 @@ function updateOverlays() {
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) {
@ -358,7 +363,7 @@ function updateOverlays() {
// 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));
@ -369,12 +374,12 @@ function updateOverlays() {
overlay.editOverlay({
color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel),
position: target,
dimensions: 0.032 * distance
dimensions: 0.032 * distance
});
if (overlay.model) {
overlay.model.ping = pingPong;
overlay.model.editOverlay({
position: target,
position: target,
scale: 0.2 * distance, // constant apparent size
rotation: Camera.orientation
});
@ -393,7 +398,9 @@ function removeOverlays() {
selectedIds = [];
lastHoveringId = 0;
HighlightedEntity.clearOverlays();
ExtendedOverlay.some(function (overlay) { overlay.deleteOverlay(); });
ExtendedOverlay.some(function (overlay) {
overlay.deleteOverlay();
});
}
//
@ -423,12 +430,13 @@ function handleMouseMove(pickRay) { // given the pickRay, just do the hover logi
// 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;
var TRIGGER_CLICK_THRESHOLD = 0.85;
var TRIGGER_PRESS_THRESHOLD = 0.05;
function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position
var pickRay;
if (HMD.active) {
if (currentHandPressed != 0) {
if (currentHandPressed !== 0) {
pickRay = controllerComputePickRay(currentHandPressed);
} else {
// nothing should hover, so
@ -441,18 +449,18 @@ function handleMouseMoveEvent(event) { // find out which overlay (if any) is ove
handleMouseMove(pickRay);
}
function handleTriggerPressed(hand, value) {
// The idea is if you press one trigger, it is the one
// 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) {
var isPressed = value > TRIGGER_PRESS_THRESHOLD;
if (currentHandPressed === 0) {
currentHandPressed = isPressed ? hand : 0;
return;
}
if (currentHandPressed == hand) {
if (currentHandPressed === hand) {
currentHandPressed = isPressed ? hand : 0;
return;
}
}
// otherwise, the other hand is still triggered
// so do nothing.
}
@ -478,7 +486,7 @@ function makeClickHandler(hand) {
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));
@ -491,25 +499,56 @@ var button;
var buttonName = "PEOPLE";
var tablet = null;
var toolBar = null;
if (Settings.getValue("HUDUIEnabled")) {
toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
button = toolBar.addButton({
objectName: buttonName,
imageURL: Script.resolvePath("assets/images/tools/people.svg"),
visible: true,
alpha: 0.9
});
pal.fromQml.connect(fromQml);
} else {
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
button = tablet.addButton({
text: buttonName,
icon: "icons/tablet-icons/people-i.svg",
sortOrder: 7
});
tablet.fromQml.connect(fromQml);
var currentUIMode;
function onTabletScreenChanged(type, url) {
if (type !== "QML" || url !== "../Pal.qml") {
off();
}
}
// @param mode {string} "tablet" or "toolbar"
function startup(mode) {
if (mode === "toolbar") {
toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
button = toolBar.addButton({
objectName: buttonName,
imageURL: Script.resolvePath("assets/images/tools/people.svg"),
visible: true,
alpha: 0.9
});
pal.fromQml.connect(fromQml);
button.clicked.connect(onToolbarButtonClicked);
pal.visibleChanged.connect(onVisibleChanged);
pal.closed.connect(off);
} else if (mode === "tablet") {
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
button = tablet.addButton({
text: buttonName,
icon: "icons/tablet-icons/people-i.svg",
sortOrder: 7
});
tablet.fromQml.connect(fromQml);
button.clicked.connect(onTabletButtonClicked);
tablet.screenChanged.connect(onTabletScreenChanged);
} else {
print("ERROR: pal.js: bad ui mode");
}
Users.usernameFromIDReply.connect(usernameFromIDReply);
Window.domainChanged.connect(clearLocalQMLDataAndClosePAL);
Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL);
Messages.subscribe(CHANNEL);
Messages.messageReceived.connect(receiveMessage);
Users.avatarDisconnected.connect(avatarDisconnected);
currentUIMode = mode;
}
// var mode = Settings.getValue("HUDUIEnabled");
startup(HMD.active ? "tablet" : "toolbar");
var isWired = false;
var audioTimer;
var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too)
@ -521,33 +560,20 @@ function off() {
Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent);
isWired = false;
}
if (audioTimer) { Script.clearInterval(audioTimer); }
if (audioTimer) {
Script.clearInterval(audioTimer);
}
triggerMapping.disable(); // It's ok if we disable twice.
triggerPressMapping.disable(); // see above
removeOverlays();
Users.requestsDomainListData = false;
}
function onClicked() {
if (Settings.getValue("HUDUIEnabled")) {
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();
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
} else {
off();
}
pal.setVisible(!pal.visible);
} else {
tablet.loadQMLSource("../Pal.qml");
function onToolbarButtonClicked() {
if (!pal.visible) {
Users.requestsDomainListData = true;
populateUserList();
pal.raise();
isWired = true;
Script.update.connect(updateOverlays);
Controller.mousePressEvent.connect(handleMouseEvent);
@ -555,7 +581,23 @@ function onClicked() {
triggerMapping.enable();
triggerPressMapping.enable();
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
} else {
off();
}
pal.setVisible(!pal.visible);
}
function onTabletButtonClicked() {
tablet.loadQMLSource("../Pal.qml");
Users.requestsDomainListData = true;
populateUserList();
isWired = true;
Script.update.connect(updateOverlays);
Controller.mousePressEvent.connect(handleMouseEvent);
Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
triggerMapping.enable();
triggerPressMapping.enable();
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
}
//
@ -570,8 +612,8 @@ function receiveMessage(channel, messageString, senderID) {
var message = JSON.parse(messageString);
switch (message.method) {
case 'select':
if (!pal.visible) {
onClicked();
if (currentUIMode === "toolbar" && !pal.visible) {
onToolbarButtonClicked();
}
sendToQml(message); // Accepts objects, not just strings.
break;
@ -579,8 +621,6 @@ function receiveMessage(channel, messageString, senderID) {
print('Unrecognized PAL message', messageString);
}
}
Messages.subscribe(CHANNEL);
Messages.messageReceived.connect(receiveMessage);
var AVERAGING_RATIO = 0.05;
@ -638,57 +678,51 @@ function avatarDisconnected(nodeID) {
// remove from the pal list
sendToQml({method: 'avatarDisconnected', params: [nodeID]});
}
//
// Button state.
//
function onVisibleChanged() {
button.editProperties({isActive: pal.visible});
}
button.clicked.connect(onClicked);
pal.visibleChanged.connect(onVisibleChanged);
pal.closed.connect(off);
if (!Settings.getValue("HUDUIEnabled")) {
tablet.screenChanged.connect(function (type, url) {
if (type !== "QML" || url !== "../Pal.qml") {
off();
}
});
}
Users.usernameFromIDReply.connect(usernameFromIDReply);
Users.avatarDisconnected.connect(avatarDisconnected);
function clearLocalQMLDataAndClosePAL() {
sendToQml({ method: 'clearLocalQMLData' });
if (pal.visible) {
onClicked(); // Close the PAL
if (currentUIMode === "toolbar" && pal.visible) {
onToolbarButtonClicked(); // Close the PAL
}
}
Window.domainChanged.connect(clearLocalQMLDataAndClosePAL);
Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL);
function shutdown() {
if (currentUIMode === "toolbar") {
button.clicked.disconnect(onToolbarButtonClicked);
toolBar.removeButton(buttonName);
pal.visibleChanged.disconnect(onVisibleChanged);
pal.closed.disconnect(off);
} else if (currentUIMode === "tablet") {
button.clicked.disconnect(onTabletButtonClicked);
tablet.removeButton(button);
tablet.screenChanged.disconnect(onTabletScreenChanged);
}
Users.usernameFromIDReply.disconnect(usernameFromIDReply);
Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL);
Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL);
Messages.subscribe(CHANNEL);
Messages.messageReceived.disconnect(receiveMessage);
Users.avatarDisconnected.disconnect(avatarDisconnected);
off();
}
//
// Cleanup.
//
Script.scriptEnding.connect(function () {
button.clicked.disconnect(onClicked);
if (tablet) {
tablet.removeButton(button);
}
if (toolBar) {
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();
Script.scriptEnding.connect(shutdown);
HMD.displayModeChanged.connect(function () {
shutdown();
startup(HMD.active ? "tablet" : "toolbar");
});
}()); // END LOCAL_SCOPE