diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 65682deb74..d4efad9ee5 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -17,4 +17,5 @@ Script.load("headMove.js"); Script.load("inspect.js"); Script.load("lobby.js"); Script.load("notifications.js"); -Script.load("lookWithMouse.js") +Script.load("lookWithMouse.js"); +Script.load("users.js"); diff --git a/examples/editEntities.js b/examples/editEntities.js index 9568f4207f..faef875d9b 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -125,7 +125,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); browseModelsButton = toolBar.addTool({ @@ -133,7 +133,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); newCubeButton = toolBar.addTool({ @@ -142,7 +142,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); newSphereButton = toolBar.addTool({ @@ -151,7 +151,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); newLightButton = toolBar.addTool({ @@ -160,7 +160,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); newTextButton = toolBar.addTool({ @@ -169,9 +169,8 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); - } that.setActive = function(active) { @@ -196,11 +195,22 @@ var toolBar = (function () { propertiesTool.setVisible(true); Window.setFocus(); } + that.showTools(isActive); } } toolBar.selectTool(activeButton, isActive); }; + // Sets visibility of tool buttons, excluding the power button + that.showTools = function(doShow) { + toolBar.showTool(newModelButton, doShow); + toolBar.showTool(browseModelsButton, doShow); + toolBar.showTool(newCubeButton, doShow); + toolBar.showTool(newSphereButton, doShow); + toolBar.showTool(newLightButton, doShow); + toolBar.showTool(newTextButton, doShow); + }; + var RESIZE_INTERVAL = 50; var RESIZE_TIMEOUT = 120000; // 2 minutes var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL; diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index 5802625d7b..951b6704ec 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -271,6 +271,10 @@ ToolBar = function(x, y, direction) { }); } } + + this.showTool = function(tool, doShow) { + this.tools[tool].show(doShow); + } this.show = function(doShow) { for(var tool in this.tools) { diff --git a/examples/users.js b/examples/users.js new file mode 100644 index 0000000000..0274cd7321 --- /dev/null +++ b/examples/users.js @@ -0,0 +1,250 @@ +// +// users.js +// examples +// +// Created by David Rowe on 9 Mar 2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var usersWindow = (function () { + + var WINDOW_WIDTH_2D = 150, + WINDOW_MARGIN_2D = 12, + WINDOW_FOREGROUND_COLOR_2D = { red: 240, green: 240, blue: 240 }, + WINDOW_FOREGROUND_ALPHA_2D = 0.9, + WINDOW_HEADING_COLOR_2D = { red: 180, green: 180, blue: 180 }, + WINDOW_HEADING_ALPHA_2D = 0.9, + WINDOW_BACKGROUND_COLOR_2D = { red: 80, green: 80, blue: 80 }, + WINDOW_BACKGROUND_ALPHA_2D = 0.7, + windowHeight = 0, + usersPane2D, + usersHeading2D, + USERS_FONT_2D = { size: 14 }, + usersLineHeight, + usersLineSpacing, + + usersOnline, // Raw data + linesOfUsers, // Array of indexes pointing into usersOnline + + API_URL = "https://metaverse.highfidelity.io/api/v1/users?status=online", + HTTP_GET_TIMEOUT = 60000, // ms = 1 minute + usersRequest, + processUsers, + usersTimedOut, + usersTimer = null, + UPDATE_TIMEOUT = 5000, // ms = 5s + + MENU_NAME = "Tools", + MENU_ITEM = "Users Online", + MENU_ITEM_AFTER = "Chat...", + + isVisible = true, + + viewportHeight; + + function onMousePressEvent(event) { + var clickedOverlay, + numLinesBefore, + overlayX, + overlayY, + minY, + maxY, + lineClicked; + + if (!isVisible) { + return; + } + + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + if (clickedOverlay === usersPane2D) { + + overlayX = event.x - WINDOW_MARGIN_2D; + overlayY = event.y - viewportHeight + windowHeight - WINDOW_MARGIN_2D - (usersLineHeight + usersLineSpacing); + + numLinesBefore = Math.floor(overlayY / (usersLineHeight + usersLineSpacing)); + minY = numLinesBefore * (usersLineHeight + usersLineSpacing); + maxY = minY + usersLineHeight; + + lineClicked = -1; + if (minY <= overlayY && overlayY <= maxY) { + lineClicked = numLinesBefore; + } + + if (0 <= lineClicked && lineClicked < linesOfUsers.length + && overlayX <= usersOnline[linesOfUsers[lineClicked]].usernameWidth) { + //print("Go to " + usersOnline[linesOfUsers[lineClicked]].username); + location.goToUser(usersOnline[linesOfUsers[lineClicked]].username); + } + } + } + + function updateWindow() { + var displayText = "", + myUsername, + user, + i; + + myUsername = GlobalServices.username; + linesOfUsers = []; + for (i = 0; i < usersOnline.length; i += 1) { + user = usersOnline[i]; + if (user.username !== myUsername && user.online) { + usersOnline[i].usernameWidth = Overlays.textSize(usersPane2D, user.username).width; + linesOfUsers.push(i); + displayText += "\n" + user.username; + } + } + + windowHeight = (linesOfUsers.length > 0 ? linesOfUsers.length + 1 : 1) * (usersLineHeight + usersLineSpacing) + - usersLineSpacing + 2 * WINDOW_MARGIN_2D; // First or only line is for heading + + Overlays.editOverlay(usersPane2D, { + y: viewportHeight - windowHeight, + height: windowHeight, + text: displayText + }); + + Overlays.editOverlay(usersHeading2D, { + y: viewportHeight - windowHeight + WINDOW_MARGIN_2D, + text: linesOfUsers.length > 0 ? "Online" : "No users online" + }); + } + + function requestUsers() { + usersRequest = new XMLHttpRequest(); + usersRequest.open("GET", API_URL, true); + usersRequest.timeout = HTTP_GET_TIMEOUT; + usersRequest.ontimeout = usersTimedOut; + usersRequest.onreadystatechange = processUsers; + usersRequest.send(); + } + + processUsers = function () { + var response; + + if (usersRequest.readyState === usersRequest.DONE) { + if (usersRequest.status === 200) { + response = JSON.parse(usersRequest.responseText); + if (response.status !== "success") { + print("Error: Request for users status returned status = " + response.status); + usersTimer = Script.setTimeout(requestUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + if (!response.hasOwnProperty("data") || !response.data.hasOwnProperty("users")) { + print("Error: Request for users status returned invalid data"); + usersTimer = Script.setTimeout(requestUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + + usersOnline = response.data.users; + updateWindow(); + } else { + print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText); + usersTimer = Script.setTimeout(requestUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + + usersTimer = Script.setTimeout(requestUsers, UPDATE_TIMEOUT); // Update after finished processing. + } + }; + + usersTimedOut = function () { + print("Error: Request for users status timed out"); + usersTimer = Script.setTimeout(requestUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + }; + + function setVisible(visible) { + isVisible = visible; + + if (isVisible) { + if (usersTimer === null) { + requestUsers(); + } + } else { + Script.clearTimeout(usersTimer); + usersTimer = null; + } + + Overlays.editOverlay(usersPane2D, { visible: isVisible }); + Overlays.editOverlay(usersHeading2D, { visible: isVisible }); + } + + function onMenuItemEvent(event) { + if (event === MENU_ITEM) { + setVisible(Menu.isOptionChecked(MENU_ITEM)); + } + } + + function update() { + viewportHeight = Controller.getViewportDimensions().y; + Overlays.editOverlay(usersPane2D, { + y: viewportHeight - windowHeight + }); + Overlays.editOverlay(usersHeading2D, { + y: viewportHeight - windowHeight + WINDOW_MARGIN_2D + }); + } + + function setUp() { + usersPane2D = Overlays.addOverlay("text", { + bounds: { x: 0, y: 0, width: WINDOW_WIDTH_2D, height: 0 }, + topMargin: WINDOW_MARGIN_2D, + leftMargin: WINDOW_MARGIN_2D, + color: WINDOW_FOREGROUND_COLOR_2D, + alpha: WINDOW_FOREGROUND_ALPHA_2D, + backgroundColor: WINDOW_BACKGROUND_COLOR_2D, + backgroundAlpha: WINDOW_BACKGROUND_ALPHA_2D, + text: "", + font: USERS_FONT_2D, + visible: isVisible + }); + + usersHeading2D = Overlays.addOverlay("text", { + x: WINDOW_MARGIN_2D, + width: WINDOW_WIDTH_2D - 2 * WINDOW_MARGIN_2D, + height: USERS_FONT_2D.size, + topMargin: 0, + leftMargin: 0, + color: WINDOW_HEADING_COLOR_2D, + alpha: WINDOW_HEADING_ALPHA_2D, + backgroundAlpha: 0.0, + text: "No users online", + font: USERS_FONT_2D, + visible: isVisible + }); + + viewportHeight = Controller.getViewportDimensions().y; + + usersLineHeight = Math.floor(Overlays.textSize(usersPane2D, "1").height); + usersLineSpacing = Math.floor(Overlays.textSize(usersPane2D, "1\n2").height - 2 * usersLineHeight); + + Controller.mousePressEvent.connect(onMousePressEvent); + + Menu.addMenuItem({ + menuName: MENU_NAME, + menuItemName: MENU_ITEM, + afterItem: MENU_ITEM_AFTER, + isCheckable: true, + isChecked: isVisible + }); + Menu.menuItemEvent.connect(onMenuItemEvent); + + Script.update.connect(update); + + requestUsers(); + } + + function tearDown() { + Menu.removeMenuItem(MENU_NAME, MENU_ITEM); + + Script.clearTimeout(usersTimer); + Overlays.deleteOverlay(usersHeading2D); + Overlays.deleteOverlay(usersPane2D); + } + + setUp(); + Script.scriptEnding.connect(tearDown); +}()); diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index 6387b42bc3..aea8487fd3 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -55,7 +55,7 @@ public: virtual TextOverlay* createClone() const; virtual QScriptValue getProperty(const QString& property); - QSizeF textSize(const QString& test) const; // Pixels + QSizeF textSize(const QString& text) const; // Pixels private: QString _text;