From 82e0ca2423e6eed65f65542eb5922c4dd69c2d15 Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Wed, 18 Jan 2017 13:19:19 -0800 Subject: [PATCH 01/52] create users tablet button --- scripts/system/users.js | 1229 +-------------------------------------- 1 file changed, 9 insertions(+), 1220 deletions(-) diff --git a/scripts/system/users.js b/scripts/system/users.js index 8c52240aa9..a403dd7978 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -2,9 +2,8 @@ // // users.js -// examples // -// Created by David Rowe on 9 Mar 2015. +// Created by Faye Li on 18 Jan 2017. // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -12,1226 +11,16 @@ // (function() { // BEGIN LOCAL_SCOPE - -// resolve these paths immediately -var MIN_MAX_BUTTON_SVG = Script.resolvePath("assets/images/tools/min-max-toggle.svg"); -var BASE_URL = Script.resolvePath("assets/images/tools/"); - -var PopUpMenu = function (properties) { - var value = properties.value, - promptOverlay, - valueOverlay, - buttonOverlay, - optionOverlays = [], - isDisplayingOptions = false, - OPTION_MARGIN = 4, - - MIN_MAX_BUTTON_SVG_WIDTH = 17.1, - MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, - MIN_MAX_BUTTON_WIDTH = 14, - MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH; - - function positionDisplayOptions() { - var y, - i; - - y = properties.y - (properties.values.length - 1) * properties.lineHeight - OPTION_MARGIN; - - for (i = 0; i < properties.values.length; i += 1) { - Overlays.editOverlay(optionOverlays[i], { - y: y - }); - y += properties.lineHeight; - } - } - - function showDisplayOptions() { - var i, - yOffScreen = Controller.getViewportDimensions().y; - - for (i = 0; i < properties.values.length; i += 1) { - optionOverlays[i] = Overlays.addOverlay("text", { - x: properties.x + properties.promptWidth, - y: yOffScreen, - width: properties.width - properties.promptWidth, - height: properties.textHeight + OPTION_MARGIN, // Only need to add margin at top to balance descenders - topMargin: OPTION_MARGIN, - leftMargin: OPTION_MARGIN, - color: properties.optionColor, - alpha: properties.optionAlpha, - backgroundColor: properties.popupBackgroundColor, - backgroundAlpha: properties.popupBackgroundAlpha, - text: properties.displayValues[i], - font: properties.font, - visible: true - }); - } - - positionDisplayOptions(); - - isDisplayingOptions = true; - } - - function deleteDisplayOptions() { - var i; - - for (i = 0; i < optionOverlays.length; i += 1) { - Overlays.deleteOverlay(optionOverlays[i]); - } - - isDisplayingOptions = false; - } - - function handleClick(overlay) { - var clicked = false, - i; - - if (overlay === valueOverlay || overlay === buttonOverlay) { - showDisplayOptions(); - return true; - } - - if (isDisplayingOptions) { - for (i = 0; i < optionOverlays.length; i += 1) { - if (overlay === optionOverlays[i]) { - value = properties.values[i]; - Overlays.editOverlay(valueOverlay, { - text: properties.displayValues[i] - }); - clicked = true; - } - } - - deleteDisplayOptions(); - } - - return clicked; - } - - function updatePosition(x, y) { - properties.x = x; - properties.y = y; - Overlays.editOverlay(promptOverlay, { - x: x, - y: y - }); - Overlays.editOverlay(valueOverlay, { - x: x + properties.promptWidth, - y: y - OPTION_MARGIN - }); - Overlays.editOverlay(buttonOverlay, { - x: x + properties.width - MIN_MAX_BUTTON_WIDTH - 1, - y: y - OPTION_MARGIN + 1 - }); - if (isDisplayingOptions) { - positionDisplayOptions(); - } - } - - function setVisible(visible) { - Overlays.editOverlay(promptOverlay, { - visible: visible - }); - Overlays.editOverlay(valueOverlay, { - visible: visible - }); - Overlays.editOverlay(buttonOverlay, { - visible: visible - }); - } - - function tearDown() { - Overlays.deleteOverlay(promptOverlay); - Overlays.deleteOverlay(valueOverlay); - Overlays.deleteOverlay(buttonOverlay); - if (isDisplayingOptions) { - deleteDisplayOptions(); - } - } - - function getValue() { - return value; - } - - function setValue(newValue) { - var index; - - index = properties.values.indexOf(newValue); - if (index !== -1) { - value = newValue; - Overlays.editOverlay(valueOverlay, { - text: properties.displayValues[index] - }); - } - } - - promptOverlay = Overlays.addOverlay("text", { - x: properties.x, - y: properties.y, - width: properties.promptWidth, - height: properties.textHeight, - topMargin: 0, - leftMargin: 0, - color: properties.promptColor, - alpha: properties.promptAlpha, - backgroundColor: properties.promptBackgroundColor, - backgroundAlpha: properties.promptBackgroundAlpha, - text: properties.prompt, - font: properties.font, - visible: properties.visible + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + // TODO: work with Alan to make new icon art + icon: "icons/tablet-icons/people-i.svg", + text: "Users" }); - valueOverlay = Overlays.addOverlay("text", { - x: properties.x + properties.promptWidth, - y: properties.y, - width: properties.width - properties.promptWidth, - height: properties.textHeight + OPTION_MARGIN, // Only need to add margin at top to balance descenders - topMargin: OPTION_MARGIN, - leftMargin: OPTION_MARGIN, - color: properties.optionColor, - alpha: properties.optionAlpha, - backgroundColor: properties.optionBackgroundColor, - backgroundAlpha: properties.optionBackgroundAlpha, - text: properties.displayValues[properties.values.indexOf(value)], - font: properties.font, - visible: properties.visible - }); - - buttonOverlay = Overlays.addOverlay("image", { - x: properties.x + properties.width - MIN_MAX_BUTTON_WIDTH - 1, - y: properties.y, - width: MIN_MAX_BUTTON_WIDTH, - height: MIN_MAX_BUTTON_HEIGHT, - imageURL: MIN_MAX_BUTTON_SVG, - subImage: { - x: 0, - y: 0, - width: MIN_MAX_BUTTON_SVG_WIDTH, - height: MIN_MAX_BUTTON_SVG_HEIGHT / 2 - }, - //color: properties.buttonColor, - alpha: properties.buttonAlpha, - visible: properties.visible - }); - - return { - updatePosition: updatePosition, - setVisible: setVisible, - handleClick: handleClick, - tearDown: tearDown, - getValue: getValue, - setValue: setValue - }; -}; - -var usersWindow = (function () { - - var WINDOW_WIDTH = 260, - WINDOW_MARGIN = 12, - WINDOW_BASE_MARGIN = 24, // A little less is needed in order look correct - WINDOW_FONT = { - size: 12 - }, - WINDOW_FOREGROUND_COLOR = { - red: 240, - green: 240, - blue: 240 - }, - WINDOW_FOREGROUND_ALPHA = 0.95, - WINDOW_HEADING_COLOR = { - red: 180, - green: 180, - blue: 180 - }, - WINDOW_HEADING_ALPHA = 0.95, - WINDOW_BACKGROUND_COLOR = { - red: 80, - green: 80, - blue: 80 - }, - WINDOW_BACKGROUND_ALPHA = 0.8, - windowPane, - windowHeading, - - // Margin on the left and right side of the window to keep - // it from getting too close to the edge of the screen which - // is unclickable. - WINDOW_MARGIN_X = 20, - - // Window border is similar to that of edit.js. - WINDOW_MARGIN_HALF = WINDOW_MARGIN / 2, - WINDOW_BORDER_WIDTH = WINDOW_WIDTH + 2 * WINDOW_MARGIN_HALF, - WINDOW_BORDER_TOP_MARGIN = 2 * WINDOW_MARGIN_HALF, - WINDOW_BORDER_BOTTOM_MARGIN = WINDOW_MARGIN_HALF, - WINDOW_BORDER_LEFT_MARGIN = WINDOW_MARGIN_HALF, - WINDOW_BORDER_RADIUS = 4, - WINDOW_BORDER_COLOR = { red: 255, green: 255, blue: 255 }, - WINDOW_BORDER_ALPHA = 0.5, - windowBorder, - - MIN_MAX_BUTTON_SVG = BASE_URL + "min-max-toggle.svg", - MIN_MAX_BUTTON_SVG_WIDTH = 17.1, - MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, - MIN_MAX_BUTTON_WIDTH = 14, - MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH, - MIN_MAX_BUTTON_COLOR = { - red: 255, - green: 255, - blue: 255 - }, - MIN_MAX_BUTTON_ALPHA = 0.9, - minimizeButton, - SCROLLBAR_BACKGROUND_WIDTH = 12, - SCROLLBAR_BACKGROUND_COLOR = { - red: 70, - green: 70, - blue: 70 - }, - SCROLLBAR_BACKGROUND_ALPHA = 0.8, - scrollbarBackground, - SCROLLBAR_BAR_MIN_HEIGHT = 5, - SCROLLBAR_BAR_COLOR = { - red: 170, - green: 170, - blue: 170 - }, - SCROLLBAR_BAR_ALPHA = 0.8, - SCROLLBAR_BAR_SELECTED_ALPHA = 0.95, - scrollbarBar, - scrollbarBackgroundHeight, - scrollbarBarHeight, - FRIENDS_BUTTON_SPACER = 6, // Space before add/remove friends button - FRIENDS_BUTTON_SVG = BASE_URL + "add-remove-friends.svg", - FRIENDS_BUTTON_SVG_WIDTH = 107, - FRIENDS_BUTTON_SVG_HEIGHT = 27, - FRIENDS_BUTTON_WIDTH = FRIENDS_BUTTON_SVG_WIDTH, - FRIENDS_BUTTON_HEIGHT = FRIENDS_BUTTON_SVG_HEIGHT, - FRIENDS_BUTTON_COLOR = { - red: 225, - green: 225, - blue: 225 - }, - FRIENDS_BUTTON_ALPHA = 0.95, - FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends", - FRIENDS_WINDOW_WIDTH = 290, - FRIENDS_WINDOW_HEIGHT = 500, - FRIENDS_WINDOW_TITLE = "Add/Remove Friends", - friendsButton, - friendsWindow, - - OPTION_BACKGROUND_COLOR = { - red: 60, - green: 60, - blue: 60 - }, - OPTION_BACKGROUND_ALPHA = 0.1, - - DISPLAY_SPACER = 12, // Space before display control - DISPLAY_PROMPT = "Show me:", - DISPLAY_PROMPT_WIDTH = 60, - DISPLAY_EVERYONE = "everyone", - DISPLAY_FRIENDS = "friends", - DISPLAY_VALUES = [DISPLAY_EVERYONE, DISPLAY_FRIENDS], - DISPLAY_DISPLAY_VALUES = DISPLAY_VALUES, - DISPLAY_OPTIONS_BACKGROUND_COLOR = { - red: 120, - green: 120, - blue: 120 - }, - DISPLAY_OPTIONS_BACKGROUND_ALPHA = 0.9, - displayControl, - - VISIBILITY_SPACER = 6, // Space before visibility control - VISIBILITY_PROMPT = "Visible to:", - VISIBILITY_PROMPT_WIDTH = 60, - VISIBILITY_ALL = "all", - VISIBILITY_FRIENDS = "friends", - VISIBILITY_NONE = "none", - VISIBILITY_VALUES = [VISIBILITY_ALL, VISIBILITY_FRIENDS, VISIBILITY_NONE], - VISIBILITY_DISPLAY_VALUES = ["everyone", "friends", "no one"], - visibilityControl, - - windowHeight, - windowBorderHeight, - windowTextHeight, - windowLineSpacing, - windowLineHeight, // = windowTextHeight + windowLineSpacing - windowMinimumHeight, - - usersOnline, // Raw users data - linesOfUsers = [], // Array of indexes pointing into usersOnline - numUsersToDisplay = 0, - firstUserToDisplay = 0, - - API_URL = "https://metaverse.highfidelity.com/api/v1/users?status=online", - API_FRIENDS_FILTER = "&filter=friends", - HTTP_GET_TIMEOUT = 60000, // ms = 1 minute - usersRequest, - processUsers, - pollUsersTimedOut, - usersTimer = null, - USERS_UPDATE_TIMEOUT = 5000, // ms = 5s - - showMe, - myVisibility, - - MENU_NAME = "View", - MENU_ITEM = "Users Online", - MENU_ITEM_OVERLAYS = "Overlays", - MENU_ITEM_AFTER = MENU_ITEM_OVERLAYS, - - SETTING_USERS_SHOW_ME = "UsersWindow.ShowMe", - SETTING_USERS_VISIBLE_TO = "UsersWindow.VisibleTo", - SETTING_USERS_WINDOW_MINIMIZED = "UsersWindow.Minimized", - SETTING_USERS_WINDOW_OFFSET = "UsersWindow.Offset", - // +ve x, y values are offset from left, top of screen; -ve from right, bottom. - - isLoggedIn = false, - isVisible = true, - isMinimized = false, - isBorderVisible = false, - - viewport, - isMirrorDisplay = false, - isFullscreenMirror = false, - - windowPosition = {}, // Bottom left corner of window pane. - isMovingWindow = false, - movingClickOffset = { x: 0, y: 0 }, - - isUsingScrollbars = false, - isMovingScrollbar = false, - scrollbarBackgroundPosition = {}, - scrollbarBarPosition = {}, - scrollbarBarClickedAt, // 0.0 .. 1.0 - scrollbarValue = 0.0; // 0.0 .. 1.0 - - function isWindowDisabled() { - return !Menu.isOptionChecked(MENU_ITEM) || !Menu.isOptionChecked(MENU_ITEM_OVERLAYS); + function cleanup() { + tablet.removeButton(button); } - function isValueTrue(value) { - // Work around Boolean Settings values being read as string when Interface starts up but as Booleans when re-read after - // Being written if refresh script. - return value === true || value === "true"; - } - - function calculateWindowHeight() { - var AUDIO_METER_HEIGHT = 52, - MIRROR_HEIGHT = 220, - nonUsersHeight, - maxWindowHeight; - - if (isMinimized) { - windowHeight = windowTextHeight + WINDOW_MARGIN + WINDOW_BASE_MARGIN; - windowBorderHeight = windowHeight + WINDOW_BORDER_TOP_MARGIN + WINDOW_BORDER_BOTTOM_MARGIN; - return; - } - - // Reserve space for title, friends button, and option controls - nonUsersHeight = WINDOW_MARGIN + windowLineHeight - + (shouldShowFriendsButton() ? FRIENDS_BUTTON_SPACER + FRIENDS_BUTTON_HEIGHT : 0) - + DISPLAY_SPACER - + windowLineHeight + VISIBILITY_SPACER - + windowLineHeight + WINDOW_BASE_MARGIN; - - // Limit window to height of viewport above window position minus VU meter and mirror if displayed - windowHeight = linesOfUsers.length * windowLineHeight - windowLineSpacing + nonUsersHeight; - maxWindowHeight = windowPosition.y - AUDIO_METER_HEIGHT; - if (isMirrorDisplay && !isFullscreenMirror) { - maxWindowHeight -= MIRROR_HEIGHT; - } - windowHeight = Math.max(Math.min(windowHeight, maxWindowHeight), nonUsersHeight); - windowBorderHeight = windowHeight + WINDOW_BORDER_TOP_MARGIN + WINDOW_BORDER_BOTTOM_MARGIN; - - // Corresponding number of users to actually display - numUsersToDisplay = Math.max(Math.round((windowHeight - nonUsersHeight) / windowLineHeight), 0); - isUsingScrollbars = 0 < numUsersToDisplay && numUsersToDisplay < linesOfUsers.length; - if (isUsingScrollbars) { - firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); - } else { - firstUserToDisplay = 0; - scrollbarValue = 0.0; - } - } - - function saturateWindowPosition() { - windowPosition.x = Math.max(WINDOW_MARGIN_X, Math.min(viewport.x - WINDOW_WIDTH - WINDOW_MARGIN_X, windowPosition.x)); - windowPosition.y = Math.max(windowMinimumHeight, Math.min(viewport.y, windowPosition.y)); - } - - function updateOverlayPositions() { - // Overlay positions are all relative to windowPosition; windowPosition is the position of the windowPane overlay. - var windowLeft = windowPosition.x, - windowTop = windowPosition.y - windowHeight, - x, - y; - - Overlays.editOverlay(windowBorder, { - x: windowPosition.x - WINDOW_BORDER_LEFT_MARGIN, - y: windowTop - WINDOW_BORDER_TOP_MARGIN - }); - Overlays.editOverlay(windowPane, { - x: windowLeft, - y: windowTop - }); - Overlays.editOverlay(windowHeading, { - x: windowLeft + WINDOW_MARGIN, - y: windowTop + WINDOW_MARGIN - }); - - Overlays.editOverlay(minimizeButton, { - x: windowLeft + WINDOW_WIDTH - WINDOW_MARGIN / 2 - MIN_MAX_BUTTON_WIDTH, - y: windowTop + WINDOW_MARGIN - }); - - scrollbarBackgroundPosition.x = windowLeft + WINDOW_WIDTH - 0.5 * WINDOW_MARGIN - SCROLLBAR_BACKGROUND_WIDTH; - scrollbarBackgroundPosition.y = windowTop + WINDOW_MARGIN + windowTextHeight; - Overlays.editOverlay(scrollbarBackground, { - x: scrollbarBackgroundPosition.x, - y: scrollbarBackgroundPosition.y - }); - scrollbarBarPosition.y = scrollbarBackgroundPosition.y + 1 - + scrollbarValue * (scrollbarBackgroundHeight - scrollbarBarHeight - 2); - Overlays.editOverlay(scrollbarBar, { - x: scrollbarBackgroundPosition.x + 1, - y: scrollbarBarPosition.y - }); - - - x = windowLeft + WINDOW_MARGIN; - y = windowPosition.y - - DISPLAY_SPACER - - windowLineHeight - VISIBILITY_SPACER - - windowLineHeight - WINDOW_BASE_MARGIN; - if (shouldShowFriendsButton()) { - y -= FRIENDS_BUTTON_HEIGHT; - Overlays.editOverlay(friendsButton, { - x: x, - y: y - }); - y += FRIENDS_BUTTON_HEIGHT; - } - - y += DISPLAY_SPACER; - displayControl.updatePosition(x, y); - - y += windowLineHeight + VISIBILITY_SPACER; - visibilityControl.updatePosition(x, y); - } - - function updateUsersDisplay() { - var displayText = "", - user, - userText, - textWidth, - maxTextWidth, - ellipsisWidth, - reducedTextWidth, - i; - - if (!isMinimized) { - maxTextWidth = WINDOW_WIDTH - (isUsingScrollbars ? SCROLLBAR_BACKGROUND_WIDTH : 0) - 2 * WINDOW_MARGIN; - ellipsisWidth = Overlays.textSize(windowPane, "...").width; - reducedTextWidth = maxTextWidth - ellipsisWidth; - - for (i = 0; i < numUsersToDisplay; i += 1) { - user = usersOnline[linesOfUsers[firstUserToDisplay + i]]; - userText = user.text; - textWidth = user.textWidth; - - if (textWidth > maxTextWidth) { - // Trim and append "..." to fit window width - maxTextWidth = maxTextWidth - Overlays.textSize(windowPane, "...").width; - while (textWidth > reducedTextWidth) { - userText = userText.slice(0, -1); - textWidth = Overlays.textSize(windowPane, userText).width; - } - userText += "..."; - } - - displayText += "\n" + userText; - } - - displayText = displayText.slice(1); // Remove leading "\n". - - scrollbarBackgroundHeight = numUsersToDisplay * windowLineHeight - windowLineSpacing / 2; - Overlays.editOverlay(scrollbarBackground, { - height: scrollbarBackgroundHeight, - visible: isLoggedIn && isUsingScrollbars - }); - scrollbarBarHeight = Math.max(numUsersToDisplay / linesOfUsers.length * scrollbarBackgroundHeight, - SCROLLBAR_BAR_MIN_HEIGHT); - Overlays.editOverlay(scrollbarBar, { - height: scrollbarBarHeight, - visible: isLoggedIn && isUsingScrollbars - }); - } - - Overlays.editOverlay(windowBorder, { - height: windowBorderHeight - }); - - Overlays.editOverlay(windowPane, { - height: windowHeight, - text: displayText - }); - - Overlays.editOverlay(windowHeading, { - text: isLoggedIn ? (linesOfUsers.length > 0 ? "Users online" : "No users online") : "Users online - log in to view" - }); - } - - function shouldShowFriendsButton() { - return isVisible && isLoggedIn && !isMinimized; - } - - function updateOverlayVisibility() { - Overlays.editOverlay(windowBorder, { - visible: isVisible && isBorderVisible - }); - Overlays.editOverlay(windowPane, { - visible: isVisible - }); - Overlays.editOverlay(windowHeading, { - visible: isVisible - }); - Overlays.editOverlay(minimizeButton, { - visible: isVisible - }); - Overlays.editOverlay(scrollbarBackground, { - visible: isVisible && isUsingScrollbars && !isMinimized - }); - Overlays.editOverlay(scrollbarBar, { - visible: isVisible && isUsingScrollbars && !isMinimized - }); - Overlays.editOverlay(friendsButton, { - visible: shouldShowFriendsButton() - }); - displayControl.setVisible(isVisible && !isMinimized); - visibilityControl.setVisible(isVisible && !isMinimized); - } - - function checkLoggedIn() { - var wasLoggedIn = isLoggedIn; - - isLoggedIn = Account.isLoggedIn(); - if (isLoggedIn !== wasLoggedIn) { - if (wasLoggedIn) { - setMinimized(true); - calculateWindowHeight(); - updateOverlayPositions(); - updateUsersDisplay(); - } - - updateOverlayVisibility(); - } - } - - function pollUsers() { - var url = API_URL; - - if (showMe === DISPLAY_FRIENDS) { - url += API_FRIENDS_FILTER; - } - - usersRequest = new XMLHttpRequest(); - usersRequest.open("GET", url, true); - usersRequest.timeout = HTTP_GET_TIMEOUT; - usersRequest.ontimeout = pollUsersTimedOut; - usersRequest.onreadystatechange = processUsers; - usersRequest.send(); - } - - processUsers = function () { - var response, - myUsername, - user, - userText, - i; - - if (usersRequest.readyState === usersRequest.DONE) { - if (usersRequest.status === 200) { - response = JSON.parse(usersRequest.responseText); - if (response.status !== "success") { - print("Error: Request for users status returned status = " + response.status); - usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. - return; - } - if (!response.hasOwnProperty("data") || !response.data.hasOwnProperty("users")) { - print("Error: Request for users status returned invalid data"); - usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. - return; - } - - usersOnline = response.data.users; - myUsername = GlobalServices.username; - linesOfUsers = []; - for (i = 0; i < usersOnline.length; i += 1) { - user = usersOnline[i]; - if (user.username !== myUsername && user.online) { - userText = user.username; - if (user.location.root) { - userText += " @ " + user.location.root.name; - } - - usersOnline[i].text = userText; - usersOnline[i].textWidth = Overlays.textSize(windowPane, userText).width; - - linesOfUsers.push(i); - } - } - - checkLoggedIn(); - calculateWindowHeight(); - updateUsersDisplay(); - updateOverlayPositions(); - - } else { - print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText); - usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. - return; - } - - usersTimer = Script.setTimeout(pollUsers, USERS_UPDATE_TIMEOUT); // Update after finished processing. - } - }; - - pollUsersTimedOut = function () { - print("Error: Request for users status timed out"); - usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. - }; - - function setVisible(visible) { - isVisible = visible; - - if (isVisible) { - if (usersTimer === null) { - pollUsers(); - } - } else { - Script.clearTimeout(usersTimer); - usersTimer = null; - } - - updateOverlayVisibility(); - } - - function setMinimized(minimized) { - isMinimized = minimized; - Overlays.editOverlay(minimizeButton, { - subImage: { - y: isMinimized ? MIN_MAX_BUTTON_SVG_HEIGHT / 2 : 0 - } - }); - updateOverlayVisibility(); - Settings.setValue(SETTING_USERS_WINDOW_MINIMIZED, isMinimized); - } - - function onMenuItemEvent(event) { - if (event === MENU_ITEM) { - setVisible(Menu.isOptionChecked(MENU_ITEM)); - } - } - - function onFindableByChanged(event) { - if (VISIBILITY_VALUES.indexOf(event) !== -1) { - myVisibility = event; - visibilityControl.setValue(event); - Settings.setValue(SETTING_USERS_VISIBLE_TO, myVisibility); - } else { - print("Error: Unrecognized onFindableByChanged value: " + event); - } - } - - function onMousePressEvent(event) { - var clickedOverlay, - numLinesBefore, - overlayX, - overlayY, - minY, - maxY, - lineClicked, - userClicked, - delta; - - if (!isVisible || isWindowDisabled()) { - return; - } - - clickedOverlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - - if (displayControl.handleClick(clickedOverlay)) { - if (usersTimer !== null) { - Script.clearTimeout(usersTimer); - usersTimer = null; - } - pollUsers(); - showMe = displayControl.getValue(); - Settings.setValue(SETTING_USERS_SHOW_ME, showMe); - return; - } - - if (visibilityControl.handleClick(clickedOverlay)) { - myVisibility = visibilityControl.getValue(); - GlobalServices.findableBy = myVisibility; - Settings.setValue(SETTING_USERS_VISIBLE_TO, myVisibility); - return; - } - - if (clickedOverlay === windowPane) { - - overlayX = event.x - windowPosition.x - WINDOW_MARGIN; - overlayY = event.y - windowPosition.y + windowHeight - WINDOW_MARGIN - windowLineHeight; - - numLinesBefore = Math.round(overlayY / windowLineHeight); - minY = numLinesBefore * windowLineHeight; - maxY = minY + windowTextHeight; - - lineClicked = -1; - if (minY <= overlayY && overlayY <= maxY) { - lineClicked = numLinesBefore; - } - - userClicked = firstUserToDisplay + lineClicked; - - if (0 <= userClicked && userClicked < linesOfUsers.length && 0 <= overlayX - && overlayX <= usersOnline[linesOfUsers[userClicked]].textWidth) { - //print("Go to " + usersOnline[linesOfUsers[userClicked]].username); - location.goToUser(usersOnline[linesOfUsers[userClicked]].username); - } - - return; - } - - if (clickedOverlay === minimizeButton) { - setMinimized(!isMinimized); - calculateWindowHeight(); - updateOverlayPositions(); - updateUsersDisplay(); - return; - } - - if (clickedOverlay === scrollbarBar) { - scrollbarBarClickedAt = (event.y - scrollbarBarPosition.y) / scrollbarBarHeight; - Overlays.editOverlay(scrollbarBar, { - backgroundAlpha: SCROLLBAR_BAR_SELECTED_ALPHA - }); - isMovingScrollbar = true; - return; - } - - if (clickedOverlay === scrollbarBackground) { - delta = scrollbarBarHeight / (scrollbarBackgroundHeight - scrollbarBarHeight); - - if (event.y < scrollbarBarPosition.y) { - scrollbarValue = Math.max(scrollbarValue - delta, 0.0); - } else { - scrollbarValue = Math.min(scrollbarValue + delta, 1.0); - } - - firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); - updateOverlayPositions(); - updateUsersDisplay(); - return; - } - - if (clickedOverlay === friendsButton) { - if (!friendsWindow) { - friendsWindow = new OverlayWebWindow({ - title: FRIENDS_WINDOW_TITLE, - width: FRIENDS_WINDOW_WIDTH, - height: FRIENDS_WINDOW_HEIGHT, - visible: false - }); - } - friendsWindow.setURL(FRIENDS_WINDOW_URL); - friendsWindow.setVisible(true); - friendsWindow.raise(); - return; - } - - if (clickedOverlay === windowBorder) { - movingClickOffset = { - x: event.x - windowPosition.x, - y: event.y - windowPosition.y - }; - - isMovingWindow = true; - } - } - - function onMouseMoveEvent(event) { - var isVisible; - - if (!isLoggedIn || isWindowDisabled()) { - return; - } - - if (isMovingScrollbar) { - if (scrollbarBackgroundPosition.x - WINDOW_MARGIN <= event.x - && event.x <= scrollbarBackgroundPosition.x + SCROLLBAR_BACKGROUND_WIDTH + WINDOW_MARGIN - && scrollbarBackgroundPosition.y - WINDOW_MARGIN <= event.y - && event.y <= scrollbarBackgroundPosition.y + scrollbarBackgroundHeight + WINDOW_MARGIN) { - scrollbarValue = (event.y - scrollbarBarClickedAt * scrollbarBarHeight - scrollbarBackgroundPosition.y) - / (scrollbarBackgroundHeight - scrollbarBarHeight - 2); - scrollbarValue = Math.min(Math.max(scrollbarValue, 0.0), 1.0); - firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); - updateOverlayPositions(); - updateUsersDisplay(); - } else { - Overlays.editOverlay(scrollbarBar, { - backgroundAlpha: SCROLLBAR_BAR_ALPHA - }); - isMovingScrollbar = false; - } - } - - if (isMovingWindow) { - windowPosition = { - x: event.x - movingClickOffset.x, - y: event.y - movingClickOffset.y - }; - - saturateWindowPosition(); - calculateWindowHeight(); - updateOverlayPositions(); - updateUsersDisplay(); - - } else { - - isVisible = isBorderVisible; - if (isVisible) { - isVisible = windowPosition.x - WINDOW_BORDER_LEFT_MARGIN <= event.x - && event.x <= windowPosition.x - WINDOW_BORDER_LEFT_MARGIN + WINDOW_BORDER_WIDTH - && windowPosition.y - windowHeight - WINDOW_BORDER_TOP_MARGIN <= event.y - && event.y <= windowPosition.y + WINDOW_BORDER_BOTTOM_MARGIN; - } else { - isVisible = windowPosition.x <= event.x && event.x <= windowPosition.x + WINDOW_WIDTH - && windowPosition.y - windowHeight <= event.y && event.y <= windowPosition.y; - } - if (isVisible !== isBorderVisible) { - isBorderVisible = isVisible; - Overlays.editOverlay(windowBorder, { - visible: isBorderVisible - }); - } - } - } - - function onMouseReleaseEvent() { - var offset = {}; - - if (isWindowDisabled()) { - return; - } - - if (isMovingScrollbar) { - Overlays.editOverlay(scrollbarBar, { - backgroundAlpha: SCROLLBAR_BAR_ALPHA - }); - isMovingScrollbar = false; - } - - if (isMovingWindow) { - // Save offset of bottom of window to nearest edge of the window. - offset.x = (windowPosition.x + WINDOW_WIDTH / 2 < viewport.x / 2) - ? windowPosition.x : windowPosition.x - viewport.x; - offset.y = (windowPosition.y < viewport.y / 2) - ? windowPosition.y : windowPosition.y - viewport.y; - Settings.setValue(SETTING_USERS_WINDOW_OFFSET, JSON.stringify(offset)); - isMovingWindow = false; - } - } - - function onScriptUpdate() { - var oldViewport = viewport, - oldIsMirrorDisplay = isMirrorDisplay, - oldIsFullscreenMirror = isFullscreenMirror, - MIRROR_MENU_ITEM = "Mirror", - FULLSCREEN_MIRROR_MENU_ITEM = "Fullscreen Mirror"; - - if (isWindowDisabled()) { - return; - } - - viewport = Controller.getViewportDimensions(); - isMirrorDisplay = Menu.isOptionChecked(MIRROR_MENU_ITEM); - isFullscreenMirror = Menu.isOptionChecked(FULLSCREEN_MIRROR_MENU_ITEM); - - if (viewport.y !== oldViewport.y || isMirrorDisplay !== oldIsMirrorDisplay - || isFullscreenMirror !== oldIsFullscreenMirror) { - calculateWindowHeight(); - updateUsersDisplay(); - } - - if (viewport.y !== oldViewport.y) { - if (windowPosition.y > oldViewport.y / 2) { - // Maintain position w.r.t. bottom of window. - windowPosition.y = viewport.y - (oldViewport.y - windowPosition.y); - } - } - - if (viewport.x !== oldViewport.x) { - if (windowPosition.x + (WINDOW_WIDTH / 2) > oldViewport.x / 2) { - // Maintain position w.r.t. right of window. - windowPosition.x = viewport.x - (oldViewport.x - windowPosition.x); - } - } - - updateOverlayPositions(); - } - - function setUp() { - var textSizeOverlay, - offsetSetting, - offset = {}, - hmdViewport; - - textSizeOverlay = Overlays.addOverlay("text", { - font: WINDOW_FONT, - visible: false - }); - windowTextHeight = Math.floor(Overlays.textSize(textSizeOverlay, "1").height); - windowLineSpacing = Math.floor(Overlays.textSize(textSizeOverlay, "1\n2").height - 2 * windowTextHeight); - windowLineHeight = windowTextHeight + windowLineSpacing; - windowMinimumHeight = windowTextHeight + WINDOW_MARGIN + WINDOW_BASE_MARGIN; - Overlays.deleteOverlay(textSizeOverlay); - - viewport = Controller.getViewportDimensions(); - - offsetSetting = Settings.getValue(SETTING_USERS_WINDOW_OFFSET); - if (offsetSetting !== "") { - offset = JSON.parse(Settings.getValue(SETTING_USERS_WINDOW_OFFSET)); - } - if (offset.hasOwnProperty("x") && offset.hasOwnProperty("y")) { - windowPosition.x = offset.x < 0 ? viewport.x + offset.x : offset.x; - windowPosition.y = offset.y <= 0 ? viewport.y + offset.y : offset.y; - } else { - hmdViewport = Controller.getRecommendedOverlayRect(); - windowPosition = { - x: (viewport.x - hmdViewport.width) / 2, // HMD viewport is narrower than screen. - y: hmdViewport.height // HMD viewport starts at top of screen but only extends down so far. - }; - } - - saturateWindowPosition(); - calculateWindowHeight(); - - windowBorder = Overlays.addOverlay("rectangle", { - x: 0, - y: viewport.y, // Start up off-screen - width: WINDOW_BORDER_WIDTH, - height: windowBorderHeight, - radius: WINDOW_BORDER_RADIUS, - color: WINDOW_BORDER_COLOR, - alpha: WINDOW_BORDER_ALPHA, - visible: false - }); - - windowPane = Overlays.addOverlay("text", { - x: 0, - y: viewport.y, - width: WINDOW_WIDTH, - height: windowHeight, - topMargin: WINDOW_MARGIN + windowLineHeight, - leftMargin: WINDOW_MARGIN, - color: WINDOW_FOREGROUND_COLOR, - alpha: WINDOW_FOREGROUND_ALPHA, - backgroundColor: WINDOW_BACKGROUND_COLOR, - backgroundAlpha: WINDOW_BACKGROUND_ALPHA, - text: "", - font: WINDOW_FONT, - visible: false - }); - - windowHeading = Overlays.addOverlay("text", { - x: 0, - y: viewport.y, - width: WINDOW_WIDTH - 2 * WINDOW_MARGIN, - height: windowTextHeight, - topMargin: 0, - leftMargin: 0, - color: WINDOW_HEADING_COLOR, - alpha: WINDOW_HEADING_ALPHA, - backgroundAlpha: 0.0, - text: "Users online", - font: WINDOW_FONT, - visible: false - }); - - minimizeButton = Overlays.addOverlay("image", { - x: 0, - y: viewport.y, - width: MIN_MAX_BUTTON_WIDTH, - height: MIN_MAX_BUTTON_HEIGHT, - imageURL: MIN_MAX_BUTTON_SVG, - subImage: { - x: 0, - y: 0, - width: MIN_MAX_BUTTON_SVG_WIDTH, - height: MIN_MAX_BUTTON_SVG_HEIGHT / 2 - }, - color: MIN_MAX_BUTTON_COLOR, - alpha: MIN_MAX_BUTTON_ALPHA, - visible: false - }); - - scrollbarBackgroundPosition = { - x: 0, - y: viewport.y - }; - scrollbarBackground = Overlays.addOverlay("text", { - x: 0, - y: scrollbarBackgroundPosition.y, - width: SCROLLBAR_BACKGROUND_WIDTH, - height: windowTextHeight, - backgroundColor: SCROLLBAR_BACKGROUND_COLOR, - backgroundAlpha: SCROLLBAR_BACKGROUND_ALPHA, - text: "", - visible: false - }); - - scrollbarBarPosition = { - x: 0, - y: viewport.y - }; - scrollbarBar = Overlays.addOverlay("text", { - x: 0, - y: scrollbarBarPosition.y, - width: SCROLLBAR_BACKGROUND_WIDTH - 2, - height: windowTextHeight, - backgroundColor: SCROLLBAR_BAR_COLOR, - backgroundAlpha: SCROLLBAR_BAR_ALPHA, - text: "", - visible: false - }); - - friendsButton = Overlays.addOverlay("image", { - x: 0, - y: viewport.y, - width: FRIENDS_BUTTON_WIDTH, - height: FRIENDS_BUTTON_HEIGHT, - imageURL: FRIENDS_BUTTON_SVG, - subImage: { - x: 0, - y: 0, - width: FRIENDS_BUTTON_SVG_WIDTH, - height: FRIENDS_BUTTON_SVG_HEIGHT - }, - color: FRIENDS_BUTTON_COLOR, - alpha: FRIENDS_BUTTON_ALPHA, - visible: false - }); - - showMe = Settings.getValue(SETTING_USERS_SHOW_ME, ""); - if (DISPLAY_VALUES.indexOf(showMe) === -1) { - showMe = DISPLAY_EVERYONE; - } - - displayControl = new PopUpMenu({ - prompt: DISPLAY_PROMPT, - value: showMe, - values: DISPLAY_VALUES, - displayValues: DISPLAY_DISPLAY_VALUES, - x: 0, - y: viewport.y, - width: WINDOW_WIDTH - 1.5 * WINDOW_MARGIN, - promptWidth: DISPLAY_PROMPT_WIDTH, - lineHeight: windowLineHeight, - textHeight: windowTextHeight, - font: WINDOW_FONT, - promptColor: WINDOW_HEADING_COLOR, - promptAlpha: WINDOW_HEADING_ALPHA, - promptBackgroundColor: WINDOW_BACKGROUND_COLOR, - promptBackgroundAlpha: 0.0, - optionColor: WINDOW_FOREGROUND_COLOR, - optionAlpha: WINDOW_FOREGROUND_ALPHA, - optionBackgroundColor: OPTION_BACKGROUND_COLOR, - optionBackgroundAlpha: OPTION_BACKGROUND_ALPHA, - popupBackgroundColor: DISPLAY_OPTIONS_BACKGROUND_COLOR, - popupBackgroundAlpha: DISPLAY_OPTIONS_BACKGROUND_ALPHA, - buttonColor: MIN_MAX_BUTTON_COLOR, - buttonAlpha: MIN_MAX_BUTTON_ALPHA, - visible: false - }); - - myVisibility = Settings.getValue(SETTING_USERS_VISIBLE_TO, ""); - if (VISIBILITY_VALUES.indexOf(myVisibility) === -1) { - myVisibility = VISIBILITY_FRIENDS; - } - GlobalServices.findableBy = myVisibility; - - visibilityControl = new PopUpMenu({ - prompt: VISIBILITY_PROMPT, - value: myVisibility, - values: VISIBILITY_VALUES, - displayValues: VISIBILITY_DISPLAY_VALUES, - x: 0, - y: viewport.y, - width: WINDOW_WIDTH - 1.5 * WINDOW_MARGIN, - promptWidth: VISIBILITY_PROMPT_WIDTH, - lineHeight: windowLineHeight, - textHeight: windowTextHeight, - font: WINDOW_FONT, - promptColor: WINDOW_HEADING_COLOR, - promptAlpha: WINDOW_HEADING_ALPHA, - promptBackgroundColor: WINDOW_BACKGROUND_COLOR, - promptBackgroundAlpha: 0.0, - optionColor: WINDOW_FOREGROUND_COLOR, - optionAlpha: WINDOW_FOREGROUND_ALPHA, - optionBackgroundColor: OPTION_BACKGROUND_COLOR, - optionBackgroundAlpha: OPTION_BACKGROUND_ALPHA, - popupBackgroundColor: DISPLAY_OPTIONS_BACKGROUND_COLOR, - popupBackgroundAlpha: DISPLAY_OPTIONS_BACKGROUND_ALPHA, - buttonColor: MIN_MAX_BUTTON_COLOR, - buttonAlpha: MIN_MAX_BUTTON_ALPHA, - visible: false - }); - - Controller.mousePressEvent.connect(onMousePressEvent); - Controller.mouseMoveEvent.connect(onMouseMoveEvent); - Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); - - Menu.addMenuItem({ - menuName: MENU_NAME, - menuItemName: MENU_ITEM, - afterItem: MENU_ITEM_AFTER, - isCheckable: true, - isChecked: isVisible - }); - Menu.menuItemEvent.connect(onMenuItemEvent); - - GlobalServices.findableByChanged.connect(onFindableByChanged); - - Script.update.connect(onScriptUpdate); - - pollUsers(); - - // Set minimized at end - setup code does not handle `minimized == false` correctly - setMinimized(isValueTrue(Settings.getValue(SETTING_USERS_WINDOW_MINIMIZED, true))); - } - - function tearDown() { - Menu.removeMenuItem(MENU_NAME, MENU_ITEM); - - Script.clearTimeout(usersTimer); - Overlays.deleteOverlay(windowBorder); - Overlays.deleteOverlay(windowPane); - Overlays.deleteOverlay(windowHeading); - Overlays.deleteOverlay(minimizeButton); - Overlays.deleteOverlay(scrollbarBackground); - Overlays.deleteOverlay(scrollbarBar); - Overlays.deleteOverlay(friendsButton); - displayControl.tearDown(); - visibilityControl.tearDown(); - } - - setUp(); - Script.scriptEnding.connect(tearDown); -}()); - + Script.scriptEnding.connect(cleanup); }()); // END LOCAL_SCOPE From 9865f134dbe771ce873f86f4dfb1bedfafb94207 Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Wed, 18 Jan 2017 16:09:12 -0800 Subject: [PATCH 02/52] go to users.html on tablet button click --- scripts/system/users.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/system/users.js b/scripts/system/users.js index a403dd7978..5603cc591c 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -4,13 +4,14 @@ // users.js // // Created by Faye Li on 18 Jan 2017. -// Copyright 2015 High Fidelity, Inc. +// Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // (function() { // BEGIN LOCAL_SCOPE + var USERS_URL = "https://hifi-content.s3.amazonaws.com/faye/tablet-dev/users.html"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ // TODO: work with Alan to make new icon art @@ -18,7 +19,14 @@ text: "Users" }); + function onClicked() { + tablet.gotoWebScreen(USERS_URL); + } + + button.clicked.connect(onClicked); + function cleanup() { + button.clicked.disconnect(onClicked); tablet.removeButton(button); } From 75b1eba84e076b2a7f7f8c1f55dd5a429965ce70 Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Wed, 18 Jan 2017 16:10:42 -0800 Subject: [PATCH 03/52] initial commit for users.html --- scripts/system/html/users.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 scripts/system/html/users.html diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html new file mode 100644 index 0000000000..cbe69efd92 --- /dev/null +++ b/scripts/system/html/users.html @@ -0,0 +1,16 @@ + + + + + +

Hello Users

+ + \ No newline at end of file From 5ad1cc6f4d538dc9a04ea7c8f938c6d45b9f1893 Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Wed, 18 Jan 2017 18:03:16 -0800 Subject: [PATCH 04/52] end of day commit --- scripts/system/html/users.html | 20 ++++++++++++++++++++ scripts/system/users.js | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index cbe69efd92..de6a03b702 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -12,5 +12,25 @@

Hello Users

+ +
+ + \ No newline at end of file diff --git a/scripts/system/users.js b/scripts/system/users.js index 5603cc591c..76745ac86e 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -11,7 +11,7 @@ // (function() { // BEGIN LOCAL_SCOPE - var USERS_URL = "https://hifi-content.s3.amazonaws.com/faye/tablet-dev/users.html"; + var USERS_URL = Script.resolvePath("html/users.html"); var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ // TODO: work with Alan to make new icon art From 10f512306ecb2ca9b0d4c987741499f6136009a7 Mon Sep 17 00:00:00 2001 From: Faye Li Date: Thu, 19 Jan 2017 10:58:29 -0800 Subject: [PATCH 05/52] successfully polling all online users --- scripts/system/html/users.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index de6a03b702..75e79c47e8 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -23,7 +23,8 @@ $.ajax({ url: METAVERSE_API_URL, success: function(response) { - $("#dev-div").html(response); + $("#dev-div").html(JSON.stringify(response.data)); + console.log(response); } }); } From 53404caccf43d4a1c17a65a0eb0957a132186d08 Mon Sep 17 00:00:00 2001 From: Faye Li Date: Thu, 19 Jan 2017 12:15:27 -0800 Subject: [PATCH 06/52] top bar css --- scripts/system/html/users.html | 38 +++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index 75e79c47e8..4192f4b33f 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -9,11 +9,43 @@ --> + Users Online + + + -

Hello Users

- -
+
+
Users Online
+
+
+ +
+
From bbed26c8fa79e8b1a11ac7550a50916b7e6c60d5 Mon Sep 17 00:00:00 2001 From: Faye Li Date: Thu, 19 Jan 2017 14:34:28 -0800 Subject: [PATCH 10/52] display all online users --- scripts/system/html/users.html | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index be657f35fe..b6cf8fb553 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -12,7 +12,7 @@ Users Online - + @@ -51,19 +57,28 @@
-
+
+ \ No newline at end of file diff --git a/scripts/system/users.js b/scripts/system/users.js index 76745ac86e..c55bccf961 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -23,7 +23,12 @@ tablet.gotoWebScreen(USERS_URL); } + function onWebEventReceived(event) { + print(event); + } + button.clicked.connect(onClicked); + tablet.webEventReceived.connect(onWebEventReceived); function cleanup() { button.clicked.disconnect(onClicked); From 763028dad4a482e0da294ad74f995ae486c39c81 Mon Sep 17 00:00:00 2001 From: Faye Li Date: Thu, 19 Jan 2017 15:46:02 -0800 Subject: [PATCH 12/52] refresh icon --- scripts/system/html/users.html | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index 703abb9638..79590f6443 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -29,7 +29,7 @@ font-weight: bold; } - .top-bar .container{ + .top-bar .container { display: flex; justify-content: space-between; align-items: center; @@ -38,6 +38,11 @@ height: 100%; } + #refresh-button { + width: 24px; + height: 24px; + } + .main { padding: 30px; } @@ -53,7 +58,7 @@
Users Online
- +
@@ -90,9 +95,9 @@ $(document).ready(function() { $("#dev-div").html("ready"); // Send a ready event to hifi - EventBridge.emitWebEvent("ready"); + // EventBridge.emitWebEvent("ready"); // Listen for events from hifi - EventBridge.scriptEventReceived.connect(onScriptEventReceived); + // EventBridge.scriptEventReceived.connect(onScriptEventReceived); }); From afc85a3afe7b79e5030dc0a550472722a2ea43ab Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Fri, 20 Jan 2017 13:53:09 -0800 Subject: [PATCH 13/52] Don't display users who aren't in a domain --- scripts/system/html/users.html | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index 79590f6443..1f60675b00 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -71,8 +71,13 @@ function displayUsers(data) { $("#users-list").empty(); for (var i = 0; i < data.users.length; i++) { - console.log(data.users[i].username + " @ " + data.users[i].location.root.name); - $("#users-list").append("
" + data.users[i].username + " @ " + data.users[i].location.root.name + "
"); + // Don't display users who aren't in a domain + if (typeof data.users[i].location.root.name === "undefined") { + console.log(data.users[i].username + "is online but not in a domain"); + } else { + console.log(data.users[i].username + " @ " + data.users[i].location.root.name); + $("#users-list").append("
" + data.users[i].username + " @ " + data.users[i].location.root.name + "
"); + } } } From f8162970ec7223ed13790aa4fc07dacc6d366f03 Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Mon, 23 Jan 2017 15:24:33 -0800 Subject: [PATCH 14/52] filter out yourself from user list --- scripts/system/html/users.html | 35 +++++++++++++++++++++++++--------- scripts/system/users.js | 17 +++++++++++++++-- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index 1f60675b00..569d352ba4 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -67,6 +67,7 @@ \ No newline at end of file diff --git a/scripts/system/users.js b/scripts/system/users.js index c55bccf961..0c10b1384d 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -11,7 +11,7 @@ // (function() { // BEGIN LOCAL_SCOPE - var USERS_URL = Script.resolvePath("html/users.html"); + var USERS_URL = "https://hifi-content.s3.amazonaws.com/faye/tablet-dev/users.html"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ // TODO: work with Alan to make new icon art @@ -24,7 +24,20 @@ } function onWebEventReceived(event) { - print(event); + print("Script received a web event, its type is " + typeof event); + if (typeof event === "string") { + event = JSON.parse(event); + } + if (event.type === "ready") { + // send username to html + var myUsername = GlobalServices.username; + var object = { + "type": "sendUsername", + "data": {"username": myUsername} + }; + print("sending username: " + myUsername); + tablet.emitScriptEvent(JSON.stringify(object)); + } } button.clicked.connect(onClicked); From c7a0e609fc9d10aac529621211cff16a5559041c Mon Sep 17 00:00:00 2001 From: Faye Li Date: Wed, 25 Jan 2017 11:41:07 -0800 Subject: [PATCH 15/52] created a friends button for metaverse friends page --- scripts/defaultScripts.js | 1 + scripts/system/friends.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 scripts/system/friends.js diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index bd3131f4ff..2b52dae9b2 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -15,6 +15,7 @@ var DEFAULT_SCRIPTS = [ "system/progress.js", "system/away.js", "system/users.js", + "system/friends.js", "system/mute.js", "system/goto.js", "system/hmd.js", diff --git a/scripts/system/friends.js b/scripts/system/friends.js new file mode 100644 index 0000000000..6afef1579f --- /dev/null +++ b/scripts/system/friends.js @@ -0,0 +1,33 @@ +"use strict"; + +// +// friends.js +// +// Created by Faye Li on 25 Jan 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + var FRIENDS_URL = "https://metaverse.highfidelity.com/user/friends"; + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + icon: "icons/tablet-icons/people-i.svg", + text: "Friends" + }); + + function onClicked() { + tablet.gotoWebScreen(FRIENDS_URL); + } + + button.clicked.connect(onClicked); + + function cleanup() { + button.clicked.disconnect(onClicked); + tablet.removeButton(button); + } + + Script.scriptEnding.connect(cleanup); +}()); // END LOCAL_SCOPE From f02b2ef40ab03142cab93a42f18c1979580c6ea2 Mon Sep 17 00:00:00 2001 From: Faye Li Date: Wed, 25 Jan 2017 11:59:31 -0800 Subject: [PATCH 16/52] auto poll users when page loads --- scripts/system/html/users.html | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index 569d352ba4..20c020dbb8 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -115,6 +115,7 @@ $(document).ready(function() { $("#dev-div").append("

ready

"); + pollUsers(); // Listen for events from hifi EventBridge.scriptEventReceived.connect(onScriptEventReceived); // Send a ready event to hifi From ce0ee610eaf7855bb226411e0b91bb09e750eaaf Mon Sep 17 00:00:00 2001 From: Faye Li Date: Wed, 25 Jan 2017 14:32:36 -0800 Subject: [PATCH 17/52] a test on polling friends --- scripts/system/html/users-friends.html | 126 +++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 scripts/system/html/users-friends.html diff --git a/scripts/system/html/users-friends.html b/scripts/system/html/users-friends.html new file mode 100644 index 0000000000..8890b2a109 --- /dev/null +++ b/scripts/system/html/users-friends.html @@ -0,0 +1,126 @@ + + + + Users Online + + + + + + +
+
+
Users Online (Friends)
+ +
+
+
+
+
+ + + + \ No newline at end of file From 5ad522668a9e36204efb4bc29dddbe3f70178ca7 Mon Sep 17 00:00:00 2001 From: Faye Li Date: Wed, 25 Jan 2017 16:29:25 -0800 Subject: [PATCH 18/52] tabs header for everyone and friends --- scripts/system/html/users.html | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index 20c020dbb8..86f8494512 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -47,6 +47,21 @@ padding: 30px; } + .tabs { + list-style: none; + padding: 0; + margin: 0; + } + + .tabs li { + display: inline-block; + padding: 10px 15px; + } + + .tabs li.current { + background: rgba(255,255,255,0.15); + } + #users-list div { padding-top: 4px; padding-bottom: 4px; @@ -62,7 +77,14 @@
-
+
    +
  • Everyone (10)
  • +
  • Friends (2)
  • +
+
+
+
+
@@ -190,6 +191,13 @@ // Send a ready event to hifi var eventObject = {"type": "ready"}; EventBridge.emitWebEvent(JSON.stringify(eventObject)); + + // Click listener mangage friends button + $("#friends-button").click(function() { + // Send a manage friends event to hifi + eventObject = {"type": "manage-friends"}; + EventBridge.emitWebEvent(JSON.stringify(eventObject)); + }); }); diff --git a/scripts/system/users.js b/scripts/system/users.js index 0c10b1384d..7930892395 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -12,9 +12,12 @@ (function() { // BEGIN LOCAL_SCOPE var USERS_URL = "https://hifi-content.s3.amazonaws.com/faye/tablet-dev/users.html"; + var FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends"; + var FRIENDS_WINDOW_WIDTH = 290; + var FRIENDS_WINDOW_HEIGHT = 500; + var FRIENDS_WINDOW_TITLE = "Add/Remove Friends"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ - // TODO: work with Alan to make new icon art icon: "icons/tablet-icons/people-i.svg", text: "Users" }); @@ -37,7 +40,19 @@ }; print("sending username: " + myUsername); tablet.emitScriptEvent(JSON.stringify(object)); - } + } + if (event.type === "manage-friends") { + // open a web overlay to metaverse friends page + var friendsWindow = new OverlayWebWindow({ + title: FRIENDS_WINDOW_TITLE, + width: FRIENDS_WINDOW_WIDTH, + height: FRIENDS_WINDOW_HEIGHT, + visible: false + }); + friendsWindow.setURL(FRIENDS_WINDOW_URL); + friendsWindow.setVisible(true); + friendsWindow.raise(); + } } button.clicked.connect(onClicked); From 8091b0ced4bfce8d5436dd93653eaf7b074f4bed Mon Sep 17 00:00:00 2001 From: Faye Li Date: Fri, 27 Jan 2017 13:33:07 -0800 Subject: [PATCH 26/52] button style --- scripts/system/html/users.html | 39 +++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/users.html b/scripts/system/html/users.html index 947642331c..23de9cf0b0 100644 --- a/scripts/system/html/users.html +++ b/scripts/system/html/users.html @@ -81,6 +81,43 @@ padding: 2px 0px; } + input[type=button] { + font-family: 'Raleway'; + font-weight: bold; + font-size: 13px; + text-transform: uppercase; + vertical-align: top; + height: 28px; + min-width: 120px; + padding: 0px 18px; + margin-right: 6px; + border-radius: 5px; + border: none; + color: #fff; + background-color: #000; + background: linear-gradient(#343434 20%, #000 100%); + cursor: pointer; + } + + input[type=button].blue { + color: #fff; + background-color: #1080b8; + background: linear-gradient(#00b4ef 20%, #1080b8 100%); + } + + input[type=button].blue:hover { + background: linear-gradient(#00b4ef, #00b4ef); + border: none; + } + + input[type=button].blue:active { + background: linear-gradient(#1080b8, #1080b8); + } + + #friends-button { + margin: 0px 0px 15px 10px; + } + @@ -100,7 +137,7 @@
    - +
    From ea366dd227748aefd1e1d99edaa795c199b659e0 Mon Sep 17 00:00:00 2001 From: Faye Li Date: Fri, 27 Jan 2017 15:38:49 -0800 Subject: [PATCH 27/52] removed unused code --- scripts/defaultScripts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 2b52dae9b2..bd3131f4ff 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -15,7 +15,6 @@ var DEFAULT_SCRIPTS = [ "system/progress.js", "system/away.js", "system/users.js", - "system/friends.js", "system/mute.js", "system/goto.js", "system/hmd.js", From ff1c4995af7ea4b7c68cae55375a2a4a9830589f Mon Sep 17 00:00:00 2001 From: Faye Li Date: Fri, 27 Jan 2017 15:44:01 -0800 Subject: [PATCH 28/52] removed unused code --- scripts/system/friends.js | 33 ------- scripts/system/html/users-friends.html | 126 ------------------------- 2 files changed, 159 deletions(-) delete mode 100644 scripts/system/friends.js delete mode 100644 scripts/system/html/users-friends.html diff --git a/scripts/system/friends.js b/scripts/system/friends.js deleted file mode 100644 index 6afef1579f..0000000000 --- a/scripts/system/friends.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; - -// -// friends.js -// -// Created by Faye Li on 25 Jan 2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - var FRIENDS_URL = "https://metaverse.highfidelity.com/user/friends"; - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - var button = tablet.addButton({ - icon: "icons/tablet-icons/people-i.svg", - text: "Friends" - }); - - function onClicked() { - tablet.gotoWebScreen(FRIENDS_URL); - } - - button.clicked.connect(onClicked); - - function cleanup() { - button.clicked.disconnect(onClicked); - tablet.removeButton(button); - } - - Script.scriptEnding.connect(cleanup); -}()); // END LOCAL_SCOPE diff --git a/scripts/system/html/users-friends.html b/scripts/system/html/users-friends.html deleted file mode 100644 index 8890b2a109..0000000000 --- a/scripts/system/html/users-friends.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - Users Online - - - - - - -
    -
    -
    Users Online (Friends)
    - -
    -
    -
    -
    -
    - - - - \ No newline at end of file From 8f4586b9b8e66eb1317885456ed52464db50d854 Mon Sep 17 00:00:00 2001 From: humbletim Date: Sat, 28 Jan 2017 10:46:50 -0500 Subject: [PATCH 29/52] hide webview context menu when clicked --- interface/resources/QtWebEngine/UIDelegates/Menu.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/QtWebEngine/UIDelegates/Menu.qml b/interface/resources/QtWebEngine/UIDelegates/Menu.qml index 21c5e71394..5176d9d11e 100644 --- a/interface/resources/QtWebEngine/UIDelegates/Menu.qml +++ b/interface/resources/QtWebEngine/UIDelegates/Menu.qml @@ -33,6 +33,7 @@ Item { propagateComposedEvents: true acceptedButtons: "AllButtons" onClicked: { + menu.visible = false; menu.done(); mouse.accepted = false; } From 8c0d7f9e28fce4215c8f2e1ec378684640104764 Mon Sep 17 00:00:00 2001 From: humbletim Date: Sat, 28 Jan 2017 14:37:49 -0500 Subject: [PATCH 30/52] hide context menu on item click --- interface/resources/QtWebEngine/UIDelegates/MenuItem.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml index 561a8926e1..1890fcb81d 100644 --- a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml +++ b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml @@ -32,6 +32,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { + menu.visible = false; root.triggered(); menu.done(); } From 8879727d88c9e43759be1c83a9ef7bc43ad32282 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 30 Jan 2017 18:11:42 +0000 Subject: [PATCH 31/52] ability to grab tablet in edit mode --- scripts/system/controllers/handControllerGrab.js | 7 ++----- scripts/system/edit.js | 4 ---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 972d95e9e9..f4b760dbce 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -853,7 +853,7 @@ function MyController(hand) { }; this.setState = function(newState, reason) { - if (isInEditMode() && (newState !== STATE_OFF && + if ((isInEditMode() && this.grabbedEntity !== HMD.tabletID )&& (newState !== STATE_OFF && newState !== STATE_SEARCHING && newState !== STATE_OVERLAY_STYLUS_TOUCHING)) { return; @@ -1510,7 +1510,6 @@ function MyController(hand) { }; this.entityIsNearGrabbable = function(entityID, handPosition, maxDistance) { - if (!this.entityIsGrabbable(entityID)) { return false; } @@ -1703,7 +1702,7 @@ function MyController(hand) { }; this.isTablet = function (entityID) { - if (entityID === HMD.tabletID) { // XXX what's a better way to know this? + if (entityID === HMD.tabletID) { return true; } return false; @@ -2264,7 +2263,6 @@ function MyController(hand) { this.clearEquipHaptics(); this.shouldScale = false; - Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); if (this.entityActivated) { @@ -2394,7 +2392,6 @@ function MyController(hand) { this.nearGrabbing = function(deltaTime, timestamp) { this.grabPointSphereOff(); - if (this.state == STATE_NEAR_GRABBING && (!this.triggerClicked && this.secondaryReleased())) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index d49f7ad3c5..dccd097817 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -191,7 +191,6 @@ var toolBar = (function () { selectionManager.clearSelections(); entityListTool.sendUpdate(); selectionManager.setSelections([entityID]); - return entityID; } @@ -714,9 +713,6 @@ function mouseClickEvent(event) { toolBar.setActive(true); var pickRay = result.pickRay; var foundEntity = result.entityID; - if (foundEntity === HMD.tabletID) { - return; - } properties = Entities.getEntityProperties(foundEntity); if (isLocked(properties)) { if (wantDebug) { From 57a9d34cdafa15a1c02d9551758398910504d55a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 30 Jan 2017 18:33:56 +0000 Subject: [PATCH 32/52] minimize git diff --- scripts/system/controllers/handControllerGrab.js | 3 +++ scripts/system/edit.js | 1 + 2 files changed, 4 insertions(+) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index f4b760dbce..2efafa504d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1510,6 +1510,7 @@ function MyController(hand) { }; this.entityIsNearGrabbable = function(entityID, handPosition, maxDistance) { + if (!this.entityIsGrabbable(entityID)) { return false; } @@ -2263,6 +2264,7 @@ function MyController(hand) { this.clearEquipHaptics(); this.shouldScale = false; + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); if (this.entityActivated) { @@ -2392,6 +2394,7 @@ function MyController(hand) { this.nearGrabbing = function(deltaTime, timestamp) { this.grabPointSphereOff(); + if (this.state == STATE_NEAR_GRABBING && (!this.triggerClicked && this.secondaryReleased())) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index dccd097817..8f9697b060 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -191,6 +191,7 @@ var toolBar = (function () { selectionManager.clearSelections(); entityListTool.sendUpdate(); selectionManager.setSelections([entityID]); + return entityID; } From b264d84385d50984af5eefb38e00352351982ef4 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 30 Jan 2017 18:38:02 +0000 Subject: [PATCH 33/52] minimize git diff --- scripts/system/controllers/handControllerGrab.js | 6 +++--- scripts/system/edit.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2efafa504d..86820c990a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1510,7 +1510,7 @@ function MyController(hand) { }; this.entityIsNearGrabbable = function(entityID, handPosition, maxDistance) { - + if (!this.entityIsGrabbable(entityID)) { return false; } @@ -2264,7 +2264,7 @@ function MyController(hand) { this.clearEquipHaptics(); this.shouldScale = false; - + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); if (this.entityActivated) { @@ -2394,7 +2394,7 @@ function MyController(hand) { this.nearGrabbing = function(deltaTime, timestamp) { this.grabPointSphereOff(); - + if (this.state == STATE_NEAR_GRABBING && (!this.triggerClicked && this.secondaryReleased())) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 8f9697b060..40952e9de1 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -191,7 +191,7 @@ var toolBar = (function () { selectionManager.clearSelections(); entityListTool.sendUpdate(); selectionManager.setSelections([entityID]); - + return entityID; } From 0962a15a06dfdb68fbcb2201da4d98b486b45c02 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 30 Jan 2017 15:34:46 -0800 Subject: [PATCH 34/52] Fix for one-frame lag in the tablet stylus. --- interface/src/ui/overlays/ModelOverlay.cpp | 2 ++ .../entities-renderer/src/RenderableModelEntityItem.cpp | 2 +- libraries/render-utils/src/Model.cpp | 7 +++---- libraries/render-utils/src/Model.h | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index f70537a952..e17ab587f6 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -22,6 +22,7 @@ ModelOverlay::ModelOverlay() _modelTextures(QVariantMap()) { _model->init(); + _model->setSpatiallyNestableOverride(this); _isLoaded = false; } @@ -33,6 +34,7 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : _updateModel(false) { _model->init(); + _model->setSpatiallyNestableOverride(this); if (_url.isValid()) { _updateModel = true; _isLoaded = false; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 087fcda8e1..be64985fdb 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -508,7 +508,7 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointerallocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this)); - _model->setSpatiallyNestableOverride(shared_from_this()); + _model->setSpatiallyNestableOverride(this); _needsInitialSimulation = true; // If we need to change URLs, update it *after rendering* (to avoid access violations) } else if (QUrl(getModelURL()) != _model->getURL()) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 59b997b2cc..b28b2022fc 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -133,16 +133,15 @@ void Model::setRotation(const glm::quat& rotation) { updateRenderItems(); } -void Model::setSpatiallyNestableOverride(SpatiallyNestablePointer override) { +void Model::setSpatiallyNestableOverride(SpatiallyNestable* override) { _spatiallyNestableOverride = override; updateRenderItems(); } Transform Model::getTransform() const { - SpatiallyNestablePointer spatiallyNestableOverride = _spatiallyNestableOverride.lock(); - if (spatiallyNestableOverride) { + if (_spatiallyNestableOverride) { bool success; - Transform transform = spatiallyNestableOverride->getTransform(success); + Transform transform = _spatiallyNestableOverride->getTransform(success); if (success) { transform.setScale(getScale()); return transform; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 8b6992394f..dfb6822eb5 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -205,7 +205,7 @@ public: void setTranslation(const glm::vec3& translation); void setRotation(const glm::quat& rotation); - void setSpatiallyNestableOverride(SpatiallyNestablePointer ptr); + void setSpatiallyNestableOverride(SpatiallyNestable* ptr); const glm::vec3& getTranslation() const { return _translation; } const glm::quat& getRotation() const { return _rotation; } @@ -297,7 +297,7 @@ protected: glm::quat _rotation; glm::vec3 _scale; - SpatiallyNestableWeakPointer _spatiallyNestableOverride; + SpatiallyNestable* _spatiallyNestableOverride { nullptr }; glm::vec3 _offset; From c3f9663ab0addb14ed9e3c6d64fe249d7f255b9f Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 31 Jan 2017 01:25:59 +0100 Subject: [PATCH 35/52] - Fix for simulation owned entities moving to 0,0,0 after cache reload - Fix for simulation priority, use SCRIPT_GRAB_SIMULATION_PRIORITY in EntityItem::grabSimulationOwnership() --- libraries/entities/src/EntityItem.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 64bc9fbd5a..52dad5e976 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -688,6 +688,14 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef somethingChanged = true; _simulationOwner.clearCurrentOwner(); } + } else if (newSimOwner.matchesValidID(myNodeID) && !(_dirtyFlags & Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_POKE) + && !(_dirtyFlags & Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB)) { + // entity-server tells us that we have simulation ownership while we never requested this for this EntityItem, + // this could happen when the user reloads the cache and entity tree. + _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; + somethingChanged = true; + _simulationOwner.clearCurrentOwner(); + weOwnSimulation = false; } else if (_simulationOwner.set(newSimOwner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; somethingChanged = true; @@ -1278,7 +1286,7 @@ void EntityItem::grabSimulationOwnership() { auto nodeList = DependencyManager::get(); if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) { // we already own it - _simulationOwner.promotePriority(SCRIPT_POKE_SIMULATION_PRIORITY); + _simulationOwner.promotePriority(SCRIPT_GRAB_SIMULATION_PRIORITY); } else { // we don't own it yet _simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow()); From 2ce357db3a1ea99eb93b28c702cba9df2d850ac2 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 30 Jan 2017 17:11:48 -0800 Subject: [PATCH 36/52] Fix bug that causes GCC not to detect AVX2 --- libraries/shared/src/CPUDetect.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/CPUDetect.h b/libraries/shared/src/CPUDetect.h index c9d2eb649b..ea6d23d8d6 100644 --- a/libraries/shared/src/CPUDetect.h +++ b/libraries/shared/src/CPUDetect.h @@ -134,7 +134,7 @@ static inline bool cpuSupportsAVX() { result = true; } } - return result; + return result; } static inline bool cpuSupportsAVX2() { @@ -143,11 +143,18 @@ static inline bool cpuSupportsAVX2() { bool result = false; if (cpuSupportsAVX()) { - if (__get_cpuid(0x7, &eax, &ebx, &ecx, &edx) && ((ebx & MASK_AVX2) == MASK_AVX2)) { - result = true; + // Work around a bug where __get_cpuid(0x7) returns wrong values on older GCC + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77756 + if (__get_cpuid(0x0, &eax, &ebx, &ecx, &edx) && (eax >= 0x7)) { + + __cpuid_count(0x7, 0x0, eax, ebx, ecx, edx); + + if ((ebx & MASK_AVX2) == MASK_AVX2) { + result = true; + } } } - return result; + return result; } #else From 7a4a76901571a764ebaeea1de036d88ed94dcc60 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 31 Jan 2017 16:53:57 +0000 Subject: [PATCH 37/52] fix more editing bugs --- scripts/system/edit.js | 3 +++ scripts/system/libraries/entitySelectionTool.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 40952e9de1..d49f7ad3c5 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -714,6 +714,9 @@ function mouseClickEvent(event) { toolBar.setActive(true); var pickRay = result.pickRay; var foundEntity = result.entityID; + if (foundEntity === HMD.tabletID) { + return; + } properties = Entities.getEntityProperties(foundEntity); if (isLocked(properties)) { if (wantDebug) { diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 2932417d25..b9bae72d14 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1038,7 +1038,7 @@ SelectionDisplay = (function() { if (entityIntersection.intersects && (!overlayIntersection.intersects || (entityIntersection.distance < overlayIntersection.distance))) { - if (HMD.tabletID == entityIntersection.entityID) { + if (HMD.tabletID === entityIntersection.entityID) { return; } From 0f75668923c0632929c630cc1955764f75819650 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 31 Jan 2017 09:36:21 -0800 Subject: [PATCH 38/52] Moved Model setting of _spatiallyNestableOverride to constructor --- interface/src/ui/overlays/ModelOverlay.cpp | 6 ++---- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 4 ++-- libraries/entities-renderer/src/EntityTreeRenderer.h | 2 +- .../entities-renderer/src/RenderableModelEntityItem.cpp | 3 +-- libraries/render-utils/src/Model.cpp | 8 ++------ libraries/render-utils/src/Model.h | 8 +++----- 6 files changed, 11 insertions(+), 20 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index e17ab587f6..a0f7c4e824 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -18,23 +18,21 @@ QString const ModelOverlay::TYPE = "model"; ModelOverlay::ModelOverlay() - : _model(std::make_shared(std::make_shared())), + : _model(std::make_shared(std::make_shared(), nullptr, this)), _modelTextures(QVariantMap()) { _model->init(); - _model->setSpatiallyNestableOverride(this); _isLoaded = false; } ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(std::make_shared(std::make_shared())), + _model(std::make_shared(std::make_shared(), nullptr, this)), _modelTextures(QVariantMap()), _url(modelOverlay->_url), _updateModel(false) { _model->init(); - _model->setSpatiallyNestableOverride(this); if (_url.isValid()) { _updateModel = true; _isLoaded = false; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index d277fd540f..88b952de95 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -540,7 +540,7 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loadingPriority) { +ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loadingPriority, SpatiallyNestable* spatiallyNestableOverride) { ModelPointer model = nullptr; // Only create and delete models on the thread that owns the EntityTreeRenderer @@ -552,7 +552,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loading return model; } - model = std::make_shared(std::make_shared()); + model = std::make_shared(std::make_shared(), nullptr, spatiallyNestableOverride); model->setLoadingPriority(loadingPriority); model->init(); model->setURL(QUrl(url)); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 8c021ad184..395025543d 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -77,7 +77,7 @@ public: void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE ModelPointer allocateModel(const QString& url, float loadingPriority = 0.0f); + Q_INVOKABLE ModelPointer allocateModel(const QString& url, float loadingPriority = 0.0f, SpatiallyNestable* spatiallyNestableOverride = nullptr); /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index be64985fdb..751f44d816 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -507,8 +507,7 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointerallocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this)); - _model->setSpatiallyNestableOverride(this); + _model = _myRenderer->allocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this), this); _needsInitialSimulation = true; // If we need to change URLs, update it *after rendering* (to avoid access violations) } else if (QUrl(getModelURL()) != _model->getURL()) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b28b2022fc..41ac39dfa8 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -78,11 +78,12 @@ void initCollisionMaterials() { } } -Model::Model(RigPointer rig, QObject* parent) : +Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : QObject(parent), _renderGeometry(), _collisionGeometry(), _renderWatcher(_renderGeometry), + _spatiallyNestableOverride(spatiallyNestableOverride), _translation(0.0f), _rotation(), _scale(1.0f, 1.0f, 1.0f), @@ -133,11 +134,6 @@ void Model::setRotation(const glm::quat& rotation) { updateRenderItems(); } -void Model::setSpatiallyNestableOverride(SpatiallyNestable* override) { - _spatiallyNestableOverride = override; - updateRenderItems(); -} - Transform Model::getTransform() const { if (_spatiallyNestableOverride) { bool success; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index dfb6822eb5..301a4592de 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -67,7 +67,7 @@ public: static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; } - Model(RigPointer rig, QObject* parent = nullptr); + Model(RigPointer rig, QObject* parent = nullptr, SpatiallyNestable* spatiallyNestableOverride = nullptr); virtual ~Model(); inline ModelPointer getThisPointer() const { @@ -205,7 +205,6 @@ public: void setTranslation(const glm::vec3& translation); void setRotation(const glm::quat& rotation); - void setSpatiallyNestableOverride(SpatiallyNestable* ptr); const glm::vec3& getTranslation() const { return _translation; } const glm::quat& getRotation() const { return _rotation; } @@ -293,12 +292,11 @@ protected: GeometryResourceWatcher _renderWatcher; + SpatiallyNestable* _spatiallyNestableOverride; + glm::vec3 _translation; glm::quat _rotation; glm::vec3 _scale; - - SpatiallyNestable* _spatiallyNestableOverride { nullptr }; - glm::vec3 _offset; static float FAKE_DIMENSION_PLACEHOLDER; From 015aafe0fbe8de86f5d72dd7e45b2c02e16d4ade Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 30 Jan 2017 13:57:13 -0800 Subject: [PATCH 39/52] make table additions in DS settings clearer --- domain-server/resources/web/css/style.css | 4 + .../resources/web/settings/js/settings.js | 249 +++++++++++------- 2 files changed, 162 insertions(+), 91 deletions(-) diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index ad426671a4..553f408e15 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -125,6 +125,10 @@ tr.new-row { background-color: #dff0d8; } +tr.invalid-input { + background-color: #f2dede; +} + .graphable-stat { text-align: center; color: #5286BC; diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 659372267c..fbc2aefceb 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -38,14 +38,15 @@ var Settings = { DOMAIN_ID_SELECTOR: '[name="metaverse.id"]', ACCESS_TOKEN_SELECTOR: '[name="metaverse.access_token"]', PLACES_TABLE_ID: 'places-table', - FORM_ID: 'settings-form' + FORM_ID: 'settings-form', + INVALID_ROW_CLASS: 'invalid-input' }; var viewHelpers = { getFormGroup: function(keypath, setting, values, isAdvanced) { form_group = "
    "; setting_value = _(values).valueForKeyPath(keypath); @@ -891,23 +892,105 @@ function reloadSettings(callback) { }); } +function validateInputs() { + // check if any new values are bad + var tables = $('table'); + + var inputsValid = true; + + var tables = $('table'); + + // clear any current invalid rows + $('tr.' + Settings.INVALID_ROW_CLASS).removeClass(Settings.INVALID_ROW_CLASS); + + function markParentRowInvalid(rowChild) { + $(rowChild).closest('tr').addClass(Settings.INVALID_ROW_CLASS); + } + + _.each(tables, function(table) { + var inputs = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ' input[data-changed="true"]'); + + var empty = false; + + _.each(inputs, function(input){ + var inputVal = $(input).val(); + + if (inputVal.length === 0) { + empty = true + markParentRowInvalid(input); + return; + } + }); + + if (empty) { + showErrorMessage("Error", "Empty field(s)"); + inputsValid = false; + return + } + + // validate keys specificially for spaces and equality to an existing key + var newKeys = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ' td.key'); + + var keyWithSpaces = false; + var duplicateKey = false; + + _.each(newKeys, function(keyCell) { + var keyVal = $(keyCell).children('input').val(); + + if (keyVal.indexOf(' ') !== -1) { + keyWithSpaces = true; + markParentRowInvalid(keyCell); + return; + } + + // make sure we don't have duplicate keys in the table + var otherKeys = $(table).find('td.key').not(keyCell); + _.each(otherKeys, function(otherKeyCell) { + var keyInput = $(otherKeyCell).children('input'); + + if (keyInput.length) { + if ($(keyInput).val() == keyVal) { + duplicateKey = true; + } + } else if ($(otherKeyCell).html() == keyVal) { + duplicateKey = true; + } + + if (duplicateKey) { + markParentRowInvalid(keyCell); + return; + } + }); + + }); + + if (keyWithSpaces) { + showErrorMessage("Error", "Key contains spaces"); + inputsValid = false; + return + } + + if (duplicateKey) { + showErrorMessage("Error", "Two keys cannot be identical"); + inputsValid = false; + return; + } + }); + + return inputsValid; +} var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!"; function saveSettings() { - // disable any inputs not changed - $("input:not([data-changed])").each(function(){ - $(this).prop('disabled', true); - }); - // grab a JSON representation of the form via form2js - var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true); - - // check if we've set the basic http password - if so convert it to base64 + // verify that the password and confirmation match before saving var canPost = true; + if (formJSON["security"]) { var password = formJSON["security"]["http_password"]; var verify_password = formJSON["security"]["verify_http_password"]; + if (password && password.length > 0) { if (password != verify_password) { bootbox.alert({"message": "Passwords must match!", "title":"Password Error"}); @@ -919,23 +1002,46 @@ function saveSettings() { } } - console.log("----- SAVING ------"); - console.log(formJSON); + if (canPost && validateInputs()) { + // POST the form JSON to the domain-server settings.json endpoint so the settings are saved - // re-enable all inputs - $("input").each(function(){ - $(this).prop('disabled', false); - }); + // disable any inputs not changed + $("input:not([data-changed])").each(function(){ + $(this).prop('disabled', true); + }); - // remove focus from the button - $(this).blur(); + // grab a JSON representation of the form via form2js + var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true); - // POST the form JSON to the domain-server settings.json endpoint so the settings are saved - if (canPost) { + // check if we've set the basic http password - if so convert it to base64 + if (formJSON["security"]) { + var password = formJSON["security"]["http_password"]; + if (password && password.length > 0) { + formJSON["security"]["http_password"] = sha256_digest(password); + } + } + + console.log("----- SAVING ------"); + console.log(formJSON); + + // re-enable all inputs + $("input").each(function(){ + $(this).prop('disabled', false); + }); + + // remove focus from the button + $(this).blur(); + + // POST the form JSON to the domain-server settings.json endpoint so the settings are saved postSettings(formJSON); } } +// disable any inputs not changed +$("input:not([data-changed])").each(function(){ + $(this).prop('disabled', true); +}); + $('body').on('click', '.save-button', function(e){ saveSettings(); return false; @@ -1110,8 +1216,9 @@ function makeTable(setting, keypath, setting_value) { if (setting.can_add_new_categories) { html += makeTableCategoryInput(setting, numVisibleColumns); } + if (setting.can_add_new_rows || setting.can_add_new_categories) { - html += makeTableInputs(setting, {}, ""); + html += makeTableHiddenInputs(setting, {}, ""); } } html += "" @@ -1137,7 +1244,7 @@ function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, return html; } -function makeTableInputs(setting, initialValues, categoryValue) { +function makeTableHiddenInputs(setting, initialValues, categoryValue) { var html = ""; @@ -1148,7 +1255,7 @@ function makeTableInputs(setting, initialValues, categoryValue) { if (setting.key) { html += "\ - \ + \ " } @@ -1157,14 +1264,14 @@ function makeTableInputs(setting, initialValues, categoryValue) { if (col.type === "checkbox") { html += "" + - "" + ""; } else { html += "" + - "" + ""; @@ -1244,49 +1351,17 @@ function addTableRow(row) { var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS); + var input_clone = row.clone(); + if (!isArray) { - // Check key spaces - var key = row.children(".key").children("input").val() - if (key.indexOf(' ') !== -1) { - showErrorMessage("Error", "Key contains spaces") - return - } - // Check keys with the same name - var equals = false; - _.each(columns.children(".key"), function(element) { - if ($(element).text() === key) { - equals = true - return - } - }) - if (equals) { - showErrorMessage("Error", "Two keys cannot be identical") - return - } + // show the key input + var keyInput = row.children(".key").children("input"); } - // Check empty fields - var empty = false; - _.each(row.children('.' + Settings.DATA_COL_CLASS + ' input'), function(element) { - if ($(element).val().length === 0) { - empty = true - return - } - }) - - if (empty) { - showErrorMessage("Error", "Empty field(s)") - return - } - - var input_clone = row.clone() - // Change input row to data row - var table = row.parents("table") - var setting_name = table.attr("name") - var full_name = setting_name + "." + key - row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS) - row.removeClass("inputs") + var table = row.parents("table"); + var setting_name = table.attr("name"); + row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS); _.each(row.children(), function(element) { if ($(element).hasClass("numbered")) { @@ -1308,34 +1383,17 @@ function addTableRow(row) { anchor.addClass(Settings.DEL_ROW_SPAN_CLASSES) } else if ($(element).hasClass("key")) { var input = $(element).children("input") - $(element).html(input.val()) - input.remove() + input.show(); } else if ($(element).hasClass(Settings.DATA_COL_CLASS)) { - // Hide inputs - var input = $(element).find("input") - var isCheckbox = false; - var isTime = false; - if (input.hasClass("table-checkbox")) { - input = $(input).parent(); - isCheckbox = true; - } else if (input.hasClass("table-time")) { - input = $(input).parent(); - isTime = true; - } + // show inputs + var input = $(element).find("input"); + input.show(); - var val = input.val(); - if (isCheckbox) { - // don't hide the checkbox - val = $(input).find("input").is(':checked'); - } else if (isTime) { - // don't hide the time - } else { - input.attr("type", "hidden") - } + var isCheckbox = input.hasClass("table-checkbox"); if (isArray) { var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length - var key = $(element).attr('name') + var key = $(element).attr('name'); // are there multiple columns or just one? // with multiple we have an array of Objects, with one we have an array of whatever the value type is @@ -1347,17 +1405,21 @@ function addTableRow(row) { input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) } } else { - input.attr("name", full_name + "." + $(element).attr("name")) + // because the name of the setting in question requires the key + // setup a hook to change the HTML name of the element whenever the key changes + var colName = $(element).attr("name"); + keyInput.on('change', function(){ + input.attr("name", setting_name + "." + $(this).val() + "." + colName); + }); } if (isCheckbox) { $(input).find("input").attr("data-changed", "true"); } else { input.attr("data-changed", "true"); - $(element).append(val); } } else { - console.log("Unknown table element") + console.log("Unknown table element"); } }); @@ -1387,7 +1449,12 @@ function deleteTableRow($row) { $row.empty(); if (!isArray) { - $row.html(""); + if ($row.attr('name')) { + $row.html(""); + } else { + // for rows that didn't have a key, simply remove the row + $row.remove(); + } } else { if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) { // This is the last row of the category, so delete the header From 1474f22fd72e31966088d166917a1c83e170eb60 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 31 Jan 2017 10:54:46 -0800 Subject: [PATCH 40/52] don't validate category inputs for empty --- .../resources/web/settings/js/settings.js | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index fbc2aefceb..d483e8171f 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -908,7 +908,7 @@ function validateInputs() { } _.each(tables, function(table) { - var inputs = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ' input[data-changed="true"]'); + var inputs = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ':not([data-category]) input[data-changed="true"]'); var empty = false; @@ -917,6 +917,7 @@ function validateInputs() { if (inputVal.length === 0) { empty = true + markParentRowInvalid(input); return; } @@ -984,25 +985,7 @@ var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please function saveSettings() { - // verify that the password and confirmation match before saving - var canPost = true; - - if (formJSON["security"]) { - var password = formJSON["security"]["http_password"]; - var verify_password = formJSON["security"]["verify_http_password"]; - - if (password && password.length > 0) { - if (password != verify_password) { - bootbox.alert({"message": "Passwords must match!", "title":"Password Error"}); - canPost = false; - } else { - formJSON["security"]["http_password"] = sha256_digest(password); - delete formJSON["security"]["verify_http_password"]; - } - } - } - - if (canPost && validateInputs()) { + if (validateInputs()) { // POST the form JSON to the domain-server settings.json endpoint so the settings are saved // disable any inputs not changed @@ -1021,6 +1004,24 @@ function saveSettings() { } } + // verify that the password and confirmation match before saving + var canPost = true; + + if (formJSON["security"]) { + var password = formJSON["security"]["http_password"]; + var verify_password = formJSON["security"]["verify_http_password"]; + + if (password && password.length > 0) { + if (password != verify_password) { + bootbox.alert({"message": "Passwords must match!", "title":"Password Error"}); + canPost = false; + } else { + formJSON["security"]["http_password"] = sha256_digest(password); + delete formJSON["security"]["verify_http_password"]; + } + } + } + console.log("----- SAVING ------"); console.log(formJSON); @@ -1032,16 +1033,13 @@ function saveSettings() { // remove focus from the button $(this).blur(); - // POST the form JSON to the domain-server settings.json endpoint so the settings are saved - postSettings(formJSON); + if (canPost) { + // POST the form JSON to the domain-server settings.json endpoint so the settings are saved + postSettings(formJSON); + } } } -// disable any inputs not changed -$("input:not([data-changed])").each(function(){ - $(this).prop('disabled', true); -}); - $('body').on('click', '.save-button', function(e){ saveSettings(); return false; From 79cb0ba074787907827664242440ea87657a7ba3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 31 Jan 2017 11:05:57 -0800 Subject: [PATCH 41/52] fix keyboard behaviour for category tables --- domain-server/resources/web/settings/js/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index d483e8171f..22ce5b3170 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -216,8 +216,8 @@ $(document).ready(function(){ sibling = sibling.next(); } - if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { - sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click(); + // for tables with categories we add the entry and setup the new row on enter + if (sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).length) { sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).click(); // set focus to the first input in the new row From 360899887775ae01049d57ab552f4cc83e6f479a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 31 Jan 2017 21:01:03 +0100 Subject: [PATCH 42/52] use dedicated bool rather than unreliable dirtyFlags to determine if the entityItem had ever bid for simulation ownership --- libraries/entities/src/EntityItem.cpp | 15 +++++++++++++-- libraries/entities/src/EntityItem.h | 7 ++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index d77fac131d..61f082c9b6 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -688,8 +688,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef somethingChanged = true; _simulationOwner.clearCurrentOwner(); } - } else if (newSimOwner.matchesValidID(myNodeID) && !(_dirtyFlags & Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_POKE) - && !(_dirtyFlags & Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB)) { + } else if (newSimOwner.matchesValidID(myNodeID) && !_hasBidOnSimulation) { // entity-server tells us that we have simulation ownership while we never requested this for this EntityItem, // this could happen when the user reloads the cache and entity tree. _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; @@ -1279,6 +1278,7 @@ void EntityItem::pokeSimulationOwnership() { // we don't own it yet _simulationOwner.setPendingPriority(SCRIPT_POKE_SIMULATION_PRIORITY, usecTimestampNow()); } + checkForFirstSimulationBid(_simulationOwner); } void EntityItem::grabSimulationOwnership() { @@ -1291,6 +1291,7 @@ void EntityItem::grabSimulationOwnership() { // we don't own it yet _simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow()); } + checkForFirstSimulationBid(_simulationOwner); } bool EntityItem::setProperties(const EntityItemProperties& properties) { @@ -1861,6 +1862,7 @@ void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id << priority; } _simulationOwner.set(id, priority); + checkForFirstSimulationBid(_simulationOwner); } void EntityItem::setSimulationOwner(const SimulationOwner& owner) { @@ -1869,6 +1871,7 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) { } _simulationOwner.set(owner); + checkForFirstSimulationBid(_simulationOwner); } void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { @@ -1879,6 +1882,7 @@ void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { if (_simulationOwner.set(owner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } + checkForFirstSimulationBid(_simulationOwner); } void EntityItem::clearSimulationOwnership() { @@ -1895,6 +1899,7 @@ void EntityItem::clearSimulationOwnership() { void EntityItem::setPendingOwnershipPriority(quint8 priority, const quint64& timestamp) { _simulationOwner.setPendingPriority(priority, timestamp); + checkForFirstSimulationBid(_simulationOwner); } QString EntityItem::actionsToDebugString() { @@ -2152,6 +2157,12 @@ void EntityItem::setActionDataInternal(QByteArray actionData) { checkWaitingToRemove(); } +void EntityItem::checkForFirstSimulationBid(const SimulationOwner& simulationOwner) const { + if (!_hasBidOnSimulation && simulationOwner.matchesValidID(DependencyManager::get()->getSessionUUID())) { + _hasBidOnSimulation = true; + } +} + void EntityItem::serializeActions(bool& success, QByteArray& result) const { if (_objectActions.size() == 0) { success = true; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index b203de203b..98a2a1e268 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -478,6 +478,8 @@ protected: const QByteArray getActionDataInternal() const; void setActionDataInternal(QByteArray actionData); + void checkForFirstSimulationBid(const SimulationOwner& simulationOwner) const; + virtual void locationChanged(bool tellPhysics = true) override; virtual void dimensionsChanged() override; @@ -586,6 +588,9 @@ protected: static quint64 _rememberDeletedActionTime; mutable QHash _previouslyDeletedActions; + // per entity keep state if it ever bid on simulation, so that we can ignore false simulation ownership + mutable bool _hasBidOnSimulation = false; + QUuid _sourceUUID; /// the server node UUID we came from bool _clientOnly { false }; @@ -594,7 +599,7 @@ protected: // physics related changes from the network to suppress any duplicates and make // sure redundant applications are idempotent glm::vec3 _lastUpdatedPositionValue; - glm::quat _lastUpdatedRotationValue; + glm::quat _lastUpdatedRotationValue; glm::vec3 _lastUpdatedVelocityValue; glm::vec3 _lastUpdatedAngularVelocityValue; glm::vec3 _lastUpdatedAccelerationValue; From 174a7ad5bdadc72f14875281889b26fb7706ac2b Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 31 Jan 2017 22:54:58 +0100 Subject: [PATCH 43/52] Optimizations and style fixes from code review --- libraries/entities/src/EntityItem.cpp | 16 +++-------- libraries/entities/src/EntityItem.h | 27 +++++++++---------- .../entities/src/EntityScriptingInterface.cpp | 2 ++ libraries/physics/src/EntityMotionState.cpp | 2 ++ 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 61f082c9b6..3c10d0382c 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1278,7 +1278,6 @@ void EntityItem::pokeSimulationOwnership() { // we don't own it yet _simulationOwner.setPendingPriority(SCRIPT_POKE_SIMULATION_PRIORITY, usecTimestampNow()); } - checkForFirstSimulationBid(_simulationOwner); } void EntityItem::grabSimulationOwnership() { @@ -1291,7 +1290,6 @@ void EntityItem::grabSimulationOwnership() { // we don't own it yet _simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow()); } - checkForFirstSimulationBid(_simulationOwner); } bool EntityItem::setProperties(const EntityItemProperties& properties) { @@ -1862,7 +1860,6 @@ void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id << priority; } _simulationOwner.set(id, priority); - checkForFirstSimulationBid(_simulationOwner); } void EntityItem::setSimulationOwner(const SimulationOwner& owner) { @@ -1871,7 +1868,6 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) { } _simulationOwner.set(owner); - checkForFirstSimulationBid(_simulationOwner); } void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { @@ -1882,7 +1878,6 @@ void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { if (_simulationOwner.set(owner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } - checkForFirstSimulationBid(_simulationOwner); } void EntityItem::clearSimulationOwnership() { @@ -1899,7 +1894,10 @@ void EntityItem::clearSimulationOwnership() { void EntityItem::setPendingOwnershipPriority(quint8 priority, const quint64& timestamp) { _simulationOwner.setPendingPriority(priority, timestamp); - checkForFirstSimulationBid(_simulationOwner); +} + +void EntityItem::rememberHasSimulationOwnershipBid() const { + _hasBidOnSimulation = true; } QString EntityItem::actionsToDebugString() { @@ -2157,12 +2155,6 @@ void EntityItem::setActionDataInternal(QByteArray actionData) { checkWaitingToRemove(); } -void EntityItem::checkForFirstSimulationBid(const SimulationOwner& simulationOwner) const { - if (!_hasBidOnSimulation && simulationOwner.matchesValidID(DependencyManager::get()->getSessionUUID())) { - _hasBidOnSimulation = true; - } -} - void EntityItem::serializeActions(bool& success, QByteArray& result) const { if (_objectActions.size() == 0) { success = true; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 98a2a1e268..e69195d53d 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -321,6 +321,7 @@ public: void updateSimulationOwner(const SimulationOwner& owner); void clearSimulationOwnership(); void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp); + void rememberHasSimulationOwnershipBid() const; const QString& getMarketplaceID() const { return _marketplaceID; } void setMarketplaceID(const QString& value) { _marketplaceID = value; } @@ -478,8 +479,6 @@ protected: const QByteArray getActionDataInternal() const; void setActionDataInternal(QByteArray actionData); - void checkForFirstSimulationBid(const SimulationOwner& simulationOwner) const; - virtual void locationChanged(bool tellPhysics = true) override; virtual void dimensionsChanged() override; @@ -499,16 +498,16 @@ protected: mutable AABox _cachedAABox; mutable AACube _maxAACube; mutable AACube _minAACube; - mutable bool _recalcAABox = true; - mutable bool _recalcMinAACube = true; - mutable bool _recalcMaxAACube = true; + mutable bool _recalcAABox { true }; + mutable bool _recalcMinAACube { true }; + mutable bool _recalcMaxAACube { true }; float _localRenderAlpha; - float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg/m^3 + float _density { ENTITY_ITEM_DEFAULT_DENSITY }; // kg/m^3 // NOTE: _volumeMultiplier is used to allow some mass properties code exist in the EntityItem base class // rather than in all of the derived classes. If we ever collapse these classes to one we could do it a // different way. - float _volumeMultiplier = 1.0f; + float _volumeMultiplier { 1.0f }; glm::vec3 _gravity; glm::vec3 _acceleration; float _damping; @@ -518,7 +517,7 @@ protected: QString _script; /// the value of the script property QString _loadedScript; /// the value of _script when the last preload signal was sent - quint64 _scriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP }; /// the script loaded property used for forced reload + quint64 _scriptTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP }; /// the script loaded property used for forced reload QString _serverScripts; /// keep track of time when _serverScripts property was last changed @@ -526,7 +525,7 @@ protected: /// the value of _scriptTimestamp when the last preload signal was sent // NOTE: on construction we want this to be different from _scriptTimestamp so we intentionally bump it - quint64 _loadedScriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 }; + quint64 _loadedScriptTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 }; QString _collisionSoundURL; SharedSoundPointer _collisionSound; @@ -564,8 +563,8 @@ protected: uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation // these backpointers are only ever set/cleared by friends: - EntityTreeElementPointer _element = nullptr; // set by EntityTreeElement - void* _physicsInfo = nullptr; // set by EntitySimulation + EntityTreeElementPointer _element { nullptr }; // set by EntityTreeElement + void* _physicsInfo { nullptr }; // set by EntitySimulation bool _simulated; // set by EntitySimulation bool addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action); @@ -582,14 +581,14 @@ protected: // are used to keep track of and work around this situation. void checkWaitingToRemove(EntitySimulationPointer simulation = nullptr); mutable QSet _actionsToRemove; - mutable bool _actionDataDirty = false; - mutable bool _actionDataNeedsTransmit = false; + mutable bool _actionDataDirty { false }; + mutable bool _actionDataNeedsTransmit { false }; // _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag static quint64 _rememberDeletedActionTime; mutable QHash _previouslyDeletedActions; // per entity keep state if it ever bid on simulation, so that we can ignore false simulation ownership - mutable bool _hasBidOnSimulation = false; + mutable bool _hasBidOnSimulation { false }; QUuid _sourceUUID; /// the server node UUID we came from diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 6fb5d14329..85c3fc74f6 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -231,6 +231,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties // and make note of it now, so we can act on it right away. propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); entity->setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); + entity->rememberHasSimulationOwnershipBid(); } entity->setLastBroadcast(usecTimestampNow()); @@ -444,6 +445,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& // we make a bid for simulation ownership properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); entity->pokeSimulationOwnership(); + entity->rememberHasSimulationOwnershipBid(); } } if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index b0bdc34b52..02cee9a03a 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -582,6 +582,8 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS; // copy _outgoingPriority into pendingPriority... _entity->setPendingOwnershipPriority(_outgoingPriority, now); + // don't forget to remember that we have made a bid + _entity->rememberHasSimulationOwnershipBid(); // ...then reset _outgoingPriority in preparation for the next frame _outgoingPriority = 0; } else if (_outgoingPriority != _entity->getSimulationPriority()) { From 3a6a7bcc8a8d6b6593b814a807c5ee6fd002d327 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 31 Jan 2017 14:44:09 -0800 Subject: [PATCH 44/52] put tablet version of users.js into a different file --- scripts/system/tablet-users.js | 67 ++ scripts/system/users.js | 1297 ++++++++++++++++++++++++++++++-- 2 files changed, 1322 insertions(+), 42 deletions(-) create mode 100644 scripts/system/tablet-users.js diff --git a/scripts/system/tablet-users.js b/scripts/system/tablet-users.js new file mode 100644 index 0000000000..7930892395 --- /dev/null +++ b/scripts/system/tablet-users.js @@ -0,0 +1,67 @@ +"use strict"; + +// +// users.js +// +// Created by Faye Li on 18 Jan 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + var USERS_URL = "https://hifi-content.s3.amazonaws.com/faye/tablet-dev/users.html"; + var FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends"; + var FRIENDS_WINDOW_WIDTH = 290; + var FRIENDS_WINDOW_HEIGHT = 500; + var FRIENDS_WINDOW_TITLE = "Add/Remove Friends"; + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + icon: "icons/tablet-icons/people-i.svg", + text: "Users" + }); + + function onClicked() { + tablet.gotoWebScreen(USERS_URL); + } + + function onWebEventReceived(event) { + print("Script received a web event, its type is " + typeof event); + if (typeof event === "string") { + event = JSON.parse(event); + } + if (event.type === "ready") { + // send username to html + var myUsername = GlobalServices.username; + var object = { + "type": "sendUsername", + "data": {"username": myUsername} + }; + print("sending username: " + myUsername); + tablet.emitScriptEvent(JSON.stringify(object)); + } + if (event.type === "manage-friends") { + // open a web overlay to metaverse friends page + var friendsWindow = new OverlayWebWindow({ + title: FRIENDS_WINDOW_TITLE, + width: FRIENDS_WINDOW_WIDTH, + height: FRIENDS_WINDOW_HEIGHT, + visible: false + }); + friendsWindow.setURL(FRIENDS_WINDOW_URL); + friendsWindow.setVisible(true); + friendsWindow.raise(); + } + } + + button.clicked.connect(onClicked); + tablet.webEventReceived.connect(onWebEventReceived); + + function cleanup() { + button.clicked.disconnect(onClicked); + tablet.removeButton(button); + } + + Script.scriptEnding.connect(cleanup); +}()); // END LOCAL_SCOPE diff --git a/scripts/system/users.js b/scripts/system/users.js index 7930892395..009c446ff3 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -2,66 +2,1279 @@ // // users.js +// examples // -// Created by Faye Li on 18 Jan 2017. -// Copyright 2017 High Fidelity, Inc. +// 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 // +/*globals HMD, Toolbars, Script, Menu, Overlays, Tablet, Controller, Settings, OverlayWebWindow, Account, GlobalServices */ (function() { // BEGIN LOCAL_SCOPE - var USERS_URL = "https://hifi-content.s3.amazonaws.com/faye/tablet-dev/users.html"; - var FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends"; - var FRIENDS_WINDOW_WIDTH = 290; - var FRIENDS_WINDOW_HEIGHT = 500; - var FRIENDS_WINDOW_TITLE = "Add/Remove Friends"; - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - var button = tablet.addButton({ - icon: "icons/tablet-icons/people-i.svg", - text: "Users" - }); +var button; +var buttonName = "USERS"; +var toolBar = null; +var tablet = null; - function onClicked() { - tablet.gotoWebScreen(USERS_URL); +var MENU_ITEM = "Users Online"; + +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 + }); +} else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + button = tablet.addButton({ + icon: "icons/tablet-icons/users-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)}); +} +button.clicked.connect(onClicked); + +// resolve these paths immediately +var MIN_MAX_BUTTON_SVG = Script.resolvePath("assets/images/tools/min-max-toggle.svg"); +var BASE_URL = Script.resolvePath("assets/images/tools/"); + +var PopUpMenu = function (properties) { + var value = properties.value, + promptOverlay, + valueOverlay, + buttonOverlay, + optionOverlays = [], + isDisplayingOptions = false, + OPTION_MARGIN = 4, + + MIN_MAX_BUTTON_SVG_WIDTH = 17.1, + MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, + MIN_MAX_BUTTON_WIDTH = 14, + MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH; + + function positionDisplayOptions() { + var y, + i; + + y = properties.y - (properties.values.length - 1) * properties.lineHeight - OPTION_MARGIN; + + for (i = 0; i < properties.values.length; i += 1) { + Overlays.editOverlay(optionOverlays[i], { + y: y + }); + y += properties.lineHeight; + } } - function onWebEventReceived(event) { - print("Script received a web event, its type is " + typeof event); - if (typeof event === "string") { - event = JSON.parse(event); - } - if (event.type === "ready") { - // send username to html - var myUsername = GlobalServices.username; - var object = { - "type": "sendUsername", - "data": {"username": myUsername} - }; - print("sending username: " + myUsername); - tablet.emitScriptEvent(JSON.stringify(object)); - } - if (event.type === "manage-friends") { - // open a web overlay to metaverse friends page - var friendsWindow = new OverlayWebWindow({ - title: FRIENDS_WINDOW_TITLE, - width: FRIENDS_WINDOW_WIDTH, - height: FRIENDS_WINDOW_HEIGHT, - visible: false + function showDisplayOptions() { + var i, + yOffScreen = Controller.getViewportDimensions().y; + + for (i = 0; i < properties.values.length; i += 1) { + optionOverlays[i] = Overlays.addOverlay("text", { + x: properties.x + properties.promptWidth, + y: yOffScreen, + width: properties.width - properties.promptWidth, + height: properties.textHeight + OPTION_MARGIN, // Only need to add margin at top to balance descenders + topMargin: OPTION_MARGIN, + leftMargin: OPTION_MARGIN, + color: properties.optionColor, + alpha: properties.optionAlpha, + backgroundColor: properties.popupBackgroundColor, + backgroundAlpha: properties.popupBackgroundAlpha, + text: properties.displayValues[i], + font: properties.font, + visible: true }); + } + + positionDisplayOptions(); + + isDisplayingOptions = true; + } + + function deleteDisplayOptions() { + var i; + + for (i = 0; i < optionOverlays.length; i += 1) { + Overlays.deleteOverlay(optionOverlays[i]); + } + + isDisplayingOptions = false; + } + + function handleClick(overlay) { + var clicked = false, + i; + + if (overlay === valueOverlay || overlay === buttonOverlay) { + showDisplayOptions(); + return true; + } + + if (isDisplayingOptions) { + for (i = 0; i < optionOverlays.length; i += 1) { + if (overlay === optionOverlays[i]) { + value = properties.values[i]; + Overlays.editOverlay(valueOverlay, { + text: properties.displayValues[i] + }); + clicked = true; + } + } + + deleteDisplayOptions(); + } + + return clicked; + } + + function updatePosition(x, y) { + properties.x = x; + properties.y = y; + Overlays.editOverlay(promptOverlay, { + x: x, + y: y + }); + Overlays.editOverlay(valueOverlay, { + x: x + properties.promptWidth, + y: y - OPTION_MARGIN + }); + Overlays.editOverlay(buttonOverlay, { + x: x + properties.width - MIN_MAX_BUTTON_WIDTH - 1, + y: y - OPTION_MARGIN + 1 + }); + if (isDisplayingOptions) { + positionDisplayOptions(); + } + } + + function setVisible(visible) { + Overlays.editOverlay(promptOverlay, { + visible: visible + }); + Overlays.editOverlay(valueOverlay, { + visible: visible + }); + Overlays.editOverlay(buttonOverlay, { + visible: visible + }); + } + + function tearDown() { + Overlays.deleteOverlay(promptOverlay); + Overlays.deleteOverlay(valueOverlay); + Overlays.deleteOverlay(buttonOverlay); + if (isDisplayingOptions) { + deleteDisplayOptions(); + } + } + + function getValue() { + return value; + } + + function setValue(newValue) { + var index; + + index = properties.values.indexOf(newValue); + if (index !== -1) { + value = newValue; + Overlays.editOverlay(valueOverlay, { + text: properties.displayValues[index] + }); + } + } + + promptOverlay = Overlays.addOverlay("text", { + x: properties.x, + y: properties.y, + width: properties.promptWidth, + height: properties.textHeight, + topMargin: 0, + leftMargin: 0, + color: properties.promptColor, + alpha: properties.promptAlpha, + backgroundColor: properties.promptBackgroundColor, + backgroundAlpha: properties.promptBackgroundAlpha, + text: properties.prompt, + font: properties.font, + visible: properties.visible + }); + + valueOverlay = Overlays.addOverlay("text", { + x: properties.x + properties.promptWidth, + y: properties.y, + width: properties.width - properties.promptWidth, + height: properties.textHeight + OPTION_MARGIN, // Only need to add margin at top to balance descenders + topMargin: OPTION_MARGIN, + leftMargin: OPTION_MARGIN, + color: properties.optionColor, + alpha: properties.optionAlpha, + backgroundColor: properties.optionBackgroundColor, + backgroundAlpha: properties.optionBackgroundAlpha, + text: properties.displayValues[properties.values.indexOf(value)], + font: properties.font, + visible: properties.visible + }); + + buttonOverlay = Overlays.addOverlay("image", { + x: properties.x + properties.width - MIN_MAX_BUTTON_WIDTH - 1, + y: properties.y, + width: MIN_MAX_BUTTON_WIDTH, + height: MIN_MAX_BUTTON_HEIGHT, + imageURL: MIN_MAX_BUTTON_SVG, + subImage: { + x: 0, + y: 0, + width: MIN_MAX_BUTTON_SVG_WIDTH, + height: MIN_MAX_BUTTON_SVG_HEIGHT / 2 + }, + //color: properties.buttonColor, + alpha: properties.buttonAlpha, + visible: properties.visible + }); + + return { + updatePosition: updatePosition, + setVisible: setVisible, + handleClick: handleClick, + tearDown: tearDown, + getValue: getValue, + setValue: setValue + }; +}; + +var usersWindow = (function () { + + var WINDOW_WIDTH = 260, + WINDOW_MARGIN = 12, + WINDOW_BASE_MARGIN = 24, // A little less is needed in order look correct + WINDOW_FONT = { + size: 12 + }, + WINDOW_FOREGROUND_COLOR = { + red: 240, + green: 240, + blue: 240 + }, + WINDOW_FOREGROUND_ALPHA = 0.95, + WINDOW_HEADING_COLOR = { + red: 180, + green: 180, + blue: 180 + }, + WINDOW_HEADING_ALPHA = 0.95, + WINDOW_BACKGROUND_COLOR = { + red: 80, + green: 80, + blue: 80 + }, + WINDOW_BACKGROUND_ALPHA = 0.8, + windowPane, + windowHeading, + + // Margin on the left and right side of the window to keep + // it from getting too close to the edge of the screen which + // is unclickable. + WINDOW_MARGIN_X = 20, + + // Window border is similar to that of edit.js. + WINDOW_MARGIN_HALF = WINDOW_MARGIN / 2, + WINDOW_BORDER_WIDTH = WINDOW_WIDTH + 2 * WINDOW_MARGIN_HALF, + WINDOW_BORDER_TOP_MARGIN = 2 * WINDOW_MARGIN_HALF, + WINDOW_BORDER_BOTTOM_MARGIN = WINDOW_MARGIN_HALF, + WINDOW_BORDER_LEFT_MARGIN = WINDOW_MARGIN_HALF, + WINDOW_BORDER_RADIUS = 4, + WINDOW_BORDER_COLOR = { red: 255, green: 255, blue: 255 }, + WINDOW_BORDER_ALPHA = 0.5, + windowBorder, + + MIN_MAX_BUTTON_SVG = BASE_URL + "min-max-toggle.svg", + MIN_MAX_BUTTON_SVG_WIDTH = 17.1, + MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, + MIN_MAX_BUTTON_WIDTH = 14, + MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH, + MIN_MAX_BUTTON_COLOR = { + red: 255, + green: 255, + blue: 255 + }, + MIN_MAX_BUTTON_ALPHA = 0.9, + minimizeButton, + SCROLLBAR_BACKGROUND_WIDTH = 12, + SCROLLBAR_BACKGROUND_COLOR = { + red: 70, + green: 70, + blue: 70 + }, + SCROLLBAR_BACKGROUND_ALPHA = 0.8, + scrollbarBackground, + SCROLLBAR_BAR_MIN_HEIGHT = 5, + SCROLLBAR_BAR_COLOR = { + red: 170, + green: 170, + blue: 170 + }, + SCROLLBAR_BAR_ALPHA = 0.8, + SCROLLBAR_BAR_SELECTED_ALPHA = 0.95, + scrollbarBar, + scrollbarBackgroundHeight, + scrollbarBarHeight, + FRIENDS_BUTTON_SPACER = 6, // Space before add/remove friends button + FRIENDS_BUTTON_SVG = BASE_URL + "add-remove-friends.svg", + FRIENDS_BUTTON_SVG_WIDTH = 107, + FRIENDS_BUTTON_SVG_HEIGHT = 27, + FRIENDS_BUTTON_WIDTH = FRIENDS_BUTTON_SVG_WIDTH, + FRIENDS_BUTTON_HEIGHT = FRIENDS_BUTTON_SVG_HEIGHT, + FRIENDS_BUTTON_COLOR = { + red: 225, + green: 225, + blue: 225 + }, + FRIENDS_BUTTON_ALPHA = 0.95, + FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends", + FRIENDS_WINDOW_WIDTH = 290, + FRIENDS_WINDOW_HEIGHT = 500, + FRIENDS_WINDOW_TITLE = "Add/Remove Friends", + friendsButton, + friendsWindow, + + OPTION_BACKGROUND_COLOR = { + red: 60, + green: 60, + blue: 60 + }, + OPTION_BACKGROUND_ALPHA = 0.1, + + DISPLAY_SPACER = 12, // Space before display control + DISPLAY_PROMPT = "Show me:", + DISPLAY_PROMPT_WIDTH = 60, + DISPLAY_EVERYONE = "everyone", + DISPLAY_FRIENDS = "friends", + DISPLAY_VALUES = [DISPLAY_EVERYONE, DISPLAY_FRIENDS], + DISPLAY_DISPLAY_VALUES = DISPLAY_VALUES, + DISPLAY_OPTIONS_BACKGROUND_COLOR = { + red: 120, + green: 120, + blue: 120 + }, + DISPLAY_OPTIONS_BACKGROUND_ALPHA = 0.9, + displayControl, + + VISIBILITY_SPACER = 6, // Space before visibility control + VISIBILITY_PROMPT = "Visible to:", + VISIBILITY_PROMPT_WIDTH = 60, + VISIBILITY_ALL = "all", + VISIBILITY_FRIENDS = "friends", + VISIBILITY_NONE = "none", + VISIBILITY_VALUES = [VISIBILITY_ALL, VISIBILITY_FRIENDS, VISIBILITY_NONE], + VISIBILITY_DISPLAY_VALUES = ["everyone", "friends", "no one"], + visibilityControl, + + windowHeight, + windowBorderHeight, + windowTextHeight, + windowLineSpacing, + windowLineHeight, // = windowTextHeight + windowLineSpacing + windowMinimumHeight, + + usersOnline, // Raw users data + linesOfUsers = [], // Array of indexes pointing into usersOnline + numUsersToDisplay = 0, + firstUserToDisplay = 0, + + API_URL = "https://metaverse.highfidelity.com/api/v1/users?status=online", + API_FRIENDS_FILTER = "&filter=friends", + HTTP_GET_TIMEOUT = 60000, // ms = 1 minute + usersRequest, + processUsers, + pollUsersTimedOut, + usersTimer = null, + USERS_UPDATE_TIMEOUT = 5000, // ms = 5s + + showMe, + myVisibility, + + MENU_NAME = "View", + MENU_ITEM = "Users Online", + MENU_ITEM_OVERLAYS = "Overlays", + MENU_ITEM_AFTER = MENU_ITEM_OVERLAYS, + + SETTING_USERS_SHOW_ME = "UsersWindow.ShowMe", + SETTING_USERS_VISIBLE_TO = "UsersWindow.VisibleTo", + SETTING_USERS_WINDOW_MINIMIZED = "UsersWindow.Minimized", + SETTING_USERS_WINDOW_OFFSET = "UsersWindow.Offset", + // +ve x, y values are offset from left, top of screen; -ve from right, bottom. + + isLoggedIn = false, + isVisible = true, + isMinimized = false, + isBorderVisible = false, + + viewport, + isMirrorDisplay = false, + isFullscreenMirror = false, + + windowPosition = {}, // Bottom left corner of window pane. + isMovingWindow = false, + movingClickOffset = { x: 0, y: 0 }, + + isUsingScrollbars = false, + isMovingScrollbar = false, + scrollbarBackgroundPosition = {}, + scrollbarBarPosition = {}, + scrollbarBarClickedAt, // 0.0 .. 1.0 + scrollbarValue = 0.0; // 0.0 .. 1.0 + + function isWindowDisabled() { + return !Menu.isOptionChecked(MENU_ITEM) || !Menu.isOptionChecked(MENU_ITEM_OVERLAYS); + } + + function isValueTrue(value) { + // Work around Boolean Settings values being read as string when Interface starts up but as Booleans when re-read after + // Being written if refresh script. + return value === true || value === "true"; + } + + function calculateWindowHeight() { + var AUDIO_METER_HEIGHT = 52, + MIRROR_HEIGHT = 220, + nonUsersHeight, + maxWindowHeight; + + if (isMinimized) { + windowHeight = windowTextHeight + WINDOW_MARGIN + WINDOW_BASE_MARGIN; + windowBorderHeight = windowHeight + WINDOW_BORDER_TOP_MARGIN + WINDOW_BORDER_BOTTOM_MARGIN; + return; + } + + // Reserve space for title, friends button, and option controls + nonUsersHeight = WINDOW_MARGIN + windowLineHeight + + (shouldShowFriendsButton() ? FRIENDS_BUTTON_SPACER + FRIENDS_BUTTON_HEIGHT : 0) + + DISPLAY_SPACER + + windowLineHeight + VISIBILITY_SPACER + + windowLineHeight + WINDOW_BASE_MARGIN; + + // Limit window to height of viewport above window position minus VU meter and mirror if displayed + windowHeight = linesOfUsers.length * windowLineHeight - windowLineSpacing + nonUsersHeight; + maxWindowHeight = windowPosition.y - AUDIO_METER_HEIGHT; + if (isMirrorDisplay && !isFullscreenMirror) { + maxWindowHeight -= MIRROR_HEIGHT; + } + windowHeight = Math.max(Math.min(windowHeight, maxWindowHeight), nonUsersHeight); + windowBorderHeight = windowHeight + WINDOW_BORDER_TOP_MARGIN + WINDOW_BORDER_BOTTOM_MARGIN; + + // Corresponding number of users to actually display + numUsersToDisplay = Math.max(Math.round((windowHeight - nonUsersHeight) / windowLineHeight), 0); + isUsingScrollbars = 0 < numUsersToDisplay && numUsersToDisplay < linesOfUsers.length; + if (isUsingScrollbars) { + firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); + } else { + firstUserToDisplay = 0; + scrollbarValue = 0.0; + } + } + + function saturateWindowPosition() { + windowPosition.x = Math.max(WINDOW_MARGIN_X, Math.min(viewport.x - WINDOW_WIDTH - WINDOW_MARGIN_X, windowPosition.x)); + windowPosition.y = Math.max(windowMinimumHeight, Math.min(viewport.y, windowPosition.y)); + } + + function updateOverlayPositions() { + // Overlay positions are all relative to windowPosition; windowPosition is the position of the windowPane overlay. + var windowLeft = windowPosition.x, + windowTop = windowPosition.y - windowHeight, + x, + y; + + Overlays.editOverlay(windowBorder, { + x: windowPosition.x - WINDOW_BORDER_LEFT_MARGIN, + y: windowTop - WINDOW_BORDER_TOP_MARGIN + }); + Overlays.editOverlay(windowPane, { + x: windowLeft, + y: windowTop + }); + Overlays.editOverlay(windowHeading, { + x: windowLeft + WINDOW_MARGIN, + y: windowTop + WINDOW_MARGIN + }); + + Overlays.editOverlay(minimizeButton, { + x: windowLeft + WINDOW_WIDTH - WINDOW_MARGIN / 2 - MIN_MAX_BUTTON_WIDTH, + y: windowTop + WINDOW_MARGIN + }); + + scrollbarBackgroundPosition.x = windowLeft + WINDOW_WIDTH - 0.5 * WINDOW_MARGIN - SCROLLBAR_BACKGROUND_WIDTH; + scrollbarBackgroundPosition.y = windowTop + WINDOW_MARGIN + windowTextHeight; + Overlays.editOverlay(scrollbarBackground, { + x: scrollbarBackgroundPosition.x, + y: scrollbarBackgroundPosition.y + }); + scrollbarBarPosition.y = scrollbarBackgroundPosition.y + 1 + + scrollbarValue * (scrollbarBackgroundHeight - scrollbarBarHeight - 2); + Overlays.editOverlay(scrollbarBar, { + x: scrollbarBackgroundPosition.x + 1, + y: scrollbarBarPosition.y + }); + + + x = windowLeft + WINDOW_MARGIN; + y = windowPosition.y - + DISPLAY_SPACER - + windowLineHeight - VISIBILITY_SPACER - + windowLineHeight - WINDOW_BASE_MARGIN; + if (shouldShowFriendsButton()) { + y -= FRIENDS_BUTTON_HEIGHT; + Overlays.editOverlay(friendsButton, { + x: x, + y: y + }); + y += FRIENDS_BUTTON_HEIGHT; + } + + y += DISPLAY_SPACER; + displayControl.updatePosition(x, y); + + y += windowLineHeight + VISIBILITY_SPACER; + visibilityControl.updatePosition(x, y); + } + + function updateUsersDisplay() { + var displayText = "", + user, + userText, + textWidth, + maxTextWidth, + ellipsisWidth, + reducedTextWidth, + i; + + if (!isMinimized) { + maxTextWidth = WINDOW_WIDTH - (isUsingScrollbars ? SCROLLBAR_BACKGROUND_WIDTH : 0) - 2 * WINDOW_MARGIN; + ellipsisWidth = Overlays.textSize(windowPane, "...").width; + reducedTextWidth = maxTextWidth - ellipsisWidth; + + for (i = 0; i < numUsersToDisplay; i += 1) { + user = usersOnline[linesOfUsers[firstUserToDisplay + i]]; + userText = user.text; + textWidth = user.textWidth; + + if (textWidth > maxTextWidth) { + // Trim and append "..." to fit window width + maxTextWidth = maxTextWidth - Overlays.textSize(windowPane, "...").width; + while (textWidth > reducedTextWidth) { + userText = userText.slice(0, -1); + textWidth = Overlays.textSize(windowPane, userText).width; + } + userText += "..."; + } + + displayText += "\n" + userText; + } + + displayText = displayText.slice(1); // Remove leading "\n". + + scrollbarBackgroundHeight = numUsersToDisplay * windowLineHeight - windowLineSpacing / 2; + Overlays.editOverlay(scrollbarBackground, { + height: scrollbarBackgroundHeight, + visible: isLoggedIn && isUsingScrollbars + }); + scrollbarBarHeight = Math.max(numUsersToDisplay / linesOfUsers.length * scrollbarBackgroundHeight, + SCROLLBAR_BAR_MIN_HEIGHT); + Overlays.editOverlay(scrollbarBar, { + height: scrollbarBarHeight, + visible: isLoggedIn && isUsingScrollbars + }); + } + + Overlays.editOverlay(windowBorder, { + height: windowBorderHeight + }); + + Overlays.editOverlay(windowPane, { + height: windowHeight, + text: displayText + }); + + Overlays.editOverlay(windowHeading, { + text: isLoggedIn ? (linesOfUsers.length > 0 ? "Users online" : "No users online") : "Users online - log in to view" + }); + } + + function shouldShowFriendsButton() { + return isVisible && isLoggedIn && !isMinimized; + } + + function updateOverlayVisibility() { + Overlays.editOverlay(windowBorder, { + visible: isVisible && isBorderVisible + }); + Overlays.editOverlay(windowPane, { + visible: isVisible + }); + Overlays.editOverlay(windowHeading, { + visible: isVisible + }); + Overlays.editOverlay(minimizeButton, { + visible: isVisible + }); + Overlays.editOverlay(scrollbarBackground, { + visible: isVisible && isUsingScrollbars && !isMinimized + }); + Overlays.editOverlay(scrollbarBar, { + visible: isVisible && isUsingScrollbars && !isMinimized + }); + Overlays.editOverlay(friendsButton, { + visible: shouldShowFriendsButton() + }); + displayControl.setVisible(isVisible && !isMinimized); + visibilityControl.setVisible(isVisible && !isMinimized); + } + + function checkLoggedIn() { + var wasLoggedIn = isLoggedIn; + + isLoggedIn = Account.isLoggedIn(); + if (isLoggedIn !== wasLoggedIn) { + if (wasLoggedIn) { + setMinimized(true); + calculateWindowHeight(); + updateOverlayPositions(); + updateUsersDisplay(); + } + + updateOverlayVisibility(); + } + } + + function pollUsers() { + var url = API_URL; + + if (showMe === DISPLAY_FRIENDS) { + url += API_FRIENDS_FILTER; + } + + usersRequest = new XMLHttpRequest(); + usersRequest.open("GET", url, true); + usersRequest.timeout = HTTP_GET_TIMEOUT; + usersRequest.ontimeout = pollUsersTimedOut; + usersRequest.onreadystatechange = processUsers; + usersRequest.send(); + } + + processUsers = function () { + var response, + myUsername, + user, + userText, + i; + + if (usersRequest.readyState === usersRequest.DONE) { + if (usersRequest.status === 200) { + response = JSON.parse(usersRequest.responseText); + if (response.status !== "success") { + print("Error: Request for users status returned status = " + response.status); + usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + if (!response.hasOwnProperty("data") || !response.data.hasOwnProperty("users")) { + print("Error: Request for users status returned invalid data"); + usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + + usersOnline = response.data.users; + myUsername = GlobalServices.username; + linesOfUsers = []; + for (i = 0; i < usersOnline.length; i += 1) { + user = usersOnline[i]; + if (user.username !== myUsername && user.online) { + userText = user.username; + if (user.location.root) { + userText += " @ " + user.location.root.name; + } + + usersOnline[i].text = userText; + usersOnline[i].textWidth = Overlays.textSize(windowPane, userText).width; + + linesOfUsers.push(i); + } + } + + checkLoggedIn(); + calculateWindowHeight(); + updateUsersDisplay(); + updateOverlayPositions(); + + } else { + print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText); + usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + + usersTimer = Script.setTimeout(pollUsers, USERS_UPDATE_TIMEOUT); // Update after finished processing. + } + }; + + pollUsersTimedOut = function () { + print("Error: Request for users status timed out"); + usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + }; + + function setVisible(visible) { + isVisible = visible; + + if (isVisible) { + if (usersTimer === null) { + pollUsers(); + } + } else { + Script.clearTimeout(usersTimer); + usersTimer = null; + } + + updateOverlayVisibility(); + } + + function setMinimized(minimized) { + isMinimized = minimized; + Overlays.editOverlay(minimizeButton, { + subImage: { + y: isMinimized ? MIN_MAX_BUTTON_SVG_HEIGHT / 2 : 0 + } + }); + updateOverlayVisibility(); + Settings.setValue(SETTING_USERS_WINDOW_MINIMIZED, isMinimized); + } + + function onMenuItemEvent(event) { + if (event === MENU_ITEM) { + setVisible(Menu.isOptionChecked(MENU_ITEM)); + } + } + + function onFindableByChanged(event) { + if (VISIBILITY_VALUES.indexOf(event) !== -1) { + myVisibility = event; + visibilityControl.setValue(event); + Settings.setValue(SETTING_USERS_VISIBLE_TO, myVisibility); + } else { + print("Error: Unrecognized onFindableByChanged value: " + event); + } + } + + function onMousePressEvent(event) { + var clickedOverlay, + numLinesBefore, + overlayX, + overlayY, + minY, + maxY, + lineClicked, + userClicked, + delta; + + if (!isVisible || isWindowDisabled()) { + return; + } + + clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + + if (displayControl.handleClick(clickedOverlay)) { + if (usersTimer !== null) { + Script.clearTimeout(usersTimer); + usersTimer = null; + } + pollUsers(); + showMe = displayControl.getValue(); + Settings.setValue(SETTING_USERS_SHOW_ME, showMe); + return; + } + + if (visibilityControl.handleClick(clickedOverlay)) { + myVisibility = visibilityControl.getValue(); + GlobalServices.findableBy = myVisibility; + Settings.setValue(SETTING_USERS_VISIBLE_TO, myVisibility); + return; + } + + if (clickedOverlay === windowPane) { + + overlayX = event.x - windowPosition.x - WINDOW_MARGIN; + overlayY = event.y - windowPosition.y + windowHeight - WINDOW_MARGIN - windowLineHeight; + + numLinesBefore = Math.round(overlayY / windowLineHeight); + minY = numLinesBefore * windowLineHeight; + maxY = minY + windowTextHeight; + + lineClicked = -1; + if (minY <= overlayY && overlayY <= maxY) { + lineClicked = numLinesBefore; + } + + userClicked = firstUserToDisplay + lineClicked; + + if (0 <= userClicked && userClicked < linesOfUsers.length && 0 <= overlayX && + overlayX <= usersOnline[linesOfUsers[userClicked]].textWidth) { + //print("Go to " + usersOnline[linesOfUsers[userClicked]].username); + location.goToUser(usersOnline[linesOfUsers[userClicked]].username); + } + + return; + } + + if (clickedOverlay === minimizeButton) { + setMinimized(!isMinimized); + calculateWindowHeight(); + updateOverlayPositions(); + updateUsersDisplay(); + return; + } + + if (clickedOverlay === scrollbarBar) { + scrollbarBarClickedAt = (event.y - scrollbarBarPosition.y) / scrollbarBarHeight; + Overlays.editOverlay(scrollbarBar, { + backgroundAlpha: SCROLLBAR_BAR_SELECTED_ALPHA + }); + isMovingScrollbar = true; + return; + } + + if (clickedOverlay === scrollbarBackground) { + delta = scrollbarBarHeight / (scrollbarBackgroundHeight - scrollbarBarHeight); + + if (event.y < scrollbarBarPosition.y) { + scrollbarValue = Math.max(scrollbarValue - delta, 0.0); + } else { + scrollbarValue = Math.min(scrollbarValue + delta, 1.0); + } + + firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); + updateOverlayPositions(); + updateUsersDisplay(); + return; + } + + if (clickedOverlay === friendsButton) { + if (!friendsWindow) { + friendsWindow = new OverlayWebWindow({ + title: FRIENDS_WINDOW_TITLE, + width: FRIENDS_WINDOW_WIDTH, + height: FRIENDS_WINDOW_HEIGHT, + visible: false + }); + } friendsWindow.setURL(FRIENDS_WINDOW_URL); friendsWindow.setVisible(true); friendsWindow.raise(); + return; + } + + if (clickedOverlay === windowBorder) { + movingClickOffset = { + x: event.x - windowPosition.x, + y: event.y - windowPosition.y + }; + + isMovingWindow = true; } } - button.clicked.connect(onClicked); - tablet.webEventReceived.connect(onWebEventReceived); + function onMouseMoveEvent(event) { + var isVisible; - function cleanup() { - button.clicked.disconnect(onClicked); - tablet.removeButton(button); + if (!isLoggedIn || isWindowDisabled()) { + return; + } + + if (isMovingScrollbar) { + if (scrollbarBackgroundPosition.x - WINDOW_MARGIN <= event.x && + event.x <= scrollbarBackgroundPosition.x + SCROLLBAR_BACKGROUND_WIDTH + WINDOW_MARGIN && + scrollbarBackgroundPosition.y - WINDOW_MARGIN <= event.y && + event.y <= scrollbarBackgroundPosition.y + scrollbarBackgroundHeight + WINDOW_MARGIN) { + scrollbarValue = (event.y - scrollbarBarClickedAt * scrollbarBarHeight - scrollbarBackgroundPosition.y) / + (scrollbarBackgroundHeight - scrollbarBarHeight - 2); + scrollbarValue = Math.min(Math.max(scrollbarValue, 0.0), 1.0); + firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay)); + updateOverlayPositions(); + updateUsersDisplay(); + } else { + Overlays.editOverlay(scrollbarBar, { + backgroundAlpha: SCROLLBAR_BAR_ALPHA + }); + isMovingScrollbar = false; + } + } + + if (isMovingWindow) { + windowPosition = { + x: event.x - movingClickOffset.x, + y: event.y - movingClickOffset.y + }; + + saturateWindowPosition(); + calculateWindowHeight(); + updateOverlayPositions(); + updateUsersDisplay(); + + } else { + + isVisible = isBorderVisible; + if (isVisible) { + isVisible = windowPosition.x - WINDOW_BORDER_LEFT_MARGIN <= event.x && + event.x <= windowPosition.x - WINDOW_BORDER_LEFT_MARGIN + WINDOW_BORDER_WIDTH && + windowPosition.y - windowHeight - WINDOW_BORDER_TOP_MARGIN <= event.y && + event.y <= windowPosition.y + WINDOW_BORDER_BOTTOM_MARGIN; + } else { + isVisible = windowPosition.x <= event.x && event.x <= windowPosition.x + WINDOW_WIDTH && + windowPosition.y - windowHeight <= event.y && event.y <= windowPosition.y; + } + if (isVisible !== isBorderVisible) { + isBorderVisible = isVisible; + Overlays.editOverlay(windowBorder, { + visible: isBorderVisible + }); + } + } } - Script.scriptEnding.connect(cleanup); + function onMouseReleaseEvent() { + var offset = {}; + + if (isWindowDisabled()) { + return; + } + + if (isMovingScrollbar) { + Overlays.editOverlay(scrollbarBar, { + backgroundAlpha: SCROLLBAR_BAR_ALPHA + }); + isMovingScrollbar = false; + } + + if (isMovingWindow) { + // Save offset of bottom of window to nearest edge of the window. + offset.x = (windowPosition.x + WINDOW_WIDTH / 2 < viewport.x / 2) ? + windowPosition.x : windowPosition.x - viewport.x; + offset.y = (windowPosition.y < viewport.y / 2) ? + windowPosition.y : windowPosition.y - viewport.y; + Settings.setValue(SETTING_USERS_WINDOW_OFFSET, JSON.stringify(offset)); + isMovingWindow = false; + } + } + + function onScriptUpdate() { + var oldViewport = viewport, + oldIsMirrorDisplay = isMirrorDisplay, + oldIsFullscreenMirror = isFullscreenMirror, + MIRROR_MENU_ITEM = "Mirror", + FULLSCREEN_MIRROR_MENU_ITEM = "Fullscreen Mirror"; + + if (isWindowDisabled()) { + return; + } + + viewport = Controller.getViewportDimensions(); + isMirrorDisplay = Menu.isOptionChecked(MIRROR_MENU_ITEM); + isFullscreenMirror = Menu.isOptionChecked(FULLSCREEN_MIRROR_MENU_ITEM); + + if (viewport.y !== oldViewport.y || isMirrorDisplay !== oldIsMirrorDisplay || + isFullscreenMirror !== oldIsFullscreenMirror) { + calculateWindowHeight(); + updateUsersDisplay(); + } + + if (viewport.y !== oldViewport.y) { + if (windowPosition.y > oldViewport.y / 2) { + // Maintain position w.r.t. bottom of window. + windowPosition.y = viewport.y - (oldViewport.y - windowPosition.y); + } + } + + if (viewport.x !== oldViewport.x) { + if (windowPosition.x + (WINDOW_WIDTH / 2) > oldViewport.x / 2) { + // Maintain position w.r.t. right of window. + windowPosition.x = viewport.x - (oldViewport.x - windowPosition.x); + } + } + + updateOverlayPositions(); + } + + function setUp() { + var textSizeOverlay, + offsetSetting, + offset = {}, + hmdViewport; + + textSizeOverlay = Overlays.addOverlay("text", { + font: WINDOW_FONT, + visible: false + }); + windowTextHeight = Math.floor(Overlays.textSize(textSizeOverlay, "1").height); + windowLineSpacing = Math.floor(Overlays.textSize(textSizeOverlay, "1\n2").height - 2 * windowTextHeight); + windowLineHeight = windowTextHeight + windowLineSpacing; + windowMinimumHeight = windowTextHeight + WINDOW_MARGIN + WINDOW_BASE_MARGIN; + Overlays.deleteOverlay(textSizeOverlay); + + viewport = Controller.getViewportDimensions(); + + offsetSetting = Settings.getValue(SETTING_USERS_WINDOW_OFFSET); + if (offsetSetting !== "") { + offset = JSON.parse(Settings.getValue(SETTING_USERS_WINDOW_OFFSET)); + } + if (offset.hasOwnProperty("x") && offset.hasOwnProperty("y")) { + windowPosition.x = offset.x < 0 ? viewport.x + offset.x : offset.x; + windowPosition.y = offset.y <= 0 ? viewport.y + offset.y : offset.y; + } else { + hmdViewport = Controller.getRecommendedOverlayRect(); + windowPosition = { + x: (viewport.x - hmdViewport.width) / 2, // HMD viewport is narrower than screen. + y: hmdViewport.height // HMD viewport starts at top of screen but only extends down so far. + }; + } + + saturateWindowPosition(); + calculateWindowHeight(); + + windowBorder = Overlays.addOverlay("rectangle", { + x: 0, + y: viewport.y, // Start up off-screen + width: WINDOW_BORDER_WIDTH, + height: windowBorderHeight, + radius: WINDOW_BORDER_RADIUS, + color: WINDOW_BORDER_COLOR, + alpha: WINDOW_BORDER_ALPHA, + visible: false + }); + + windowPane = Overlays.addOverlay("text", { + x: 0, + y: viewport.y, + width: WINDOW_WIDTH, + height: windowHeight, + topMargin: WINDOW_MARGIN + windowLineHeight, + leftMargin: WINDOW_MARGIN, + color: WINDOW_FOREGROUND_COLOR, + alpha: WINDOW_FOREGROUND_ALPHA, + backgroundColor: WINDOW_BACKGROUND_COLOR, + backgroundAlpha: WINDOW_BACKGROUND_ALPHA, + text: "", + font: WINDOW_FONT, + visible: false + }); + + windowHeading = Overlays.addOverlay("text", { + x: 0, + y: viewport.y, + width: WINDOW_WIDTH - 2 * WINDOW_MARGIN, + height: windowTextHeight, + topMargin: 0, + leftMargin: 0, + color: WINDOW_HEADING_COLOR, + alpha: WINDOW_HEADING_ALPHA, + backgroundAlpha: 0.0, + text: "Users online", + font: WINDOW_FONT, + visible: false + }); + + minimizeButton = Overlays.addOverlay("image", { + x: 0, + y: viewport.y, + width: MIN_MAX_BUTTON_WIDTH, + height: MIN_MAX_BUTTON_HEIGHT, + imageURL: MIN_MAX_BUTTON_SVG, + subImage: { + x: 0, + y: 0, + width: MIN_MAX_BUTTON_SVG_WIDTH, + height: MIN_MAX_BUTTON_SVG_HEIGHT / 2 + }, + color: MIN_MAX_BUTTON_COLOR, + alpha: MIN_MAX_BUTTON_ALPHA, + visible: false + }); + + scrollbarBackgroundPosition = { + x: 0, + y: viewport.y + }; + scrollbarBackground = Overlays.addOverlay("text", { + x: 0, + y: scrollbarBackgroundPosition.y, + width: SCROLLBAR_BACKGROUND_WIDTH, + height: windowTextHeight, + backgroundColor: SCROLLBAR_BACKGROUND_COLOR, + backgroundAlpha: SCROLLBAR_BACKGROUND_ALPHA, + text: "", + visible: false + }); + + scrollbarBarPosition = { + x: 0, + y: viewport.y + }; + scrollbarBar = Overlays.addOverlay("text", { + x: 0, + y: scrollbarBarPosition.y, + width: SCROLLBAR_BACKGROUND_WIDTH - 2, + height: windowTextHeight, + backgroundColor: SCROLLBAR_BAR_COLOR, + backgroundAlpha: SCROLLBAR_BAR_ALPHA, + text: "", + visible: false + }); + + friendsButton = Overlays.addOverlay("image", { + x: 0, + y: viewport.y, + width: FRIENDS_BUTTON_WIDTH, + height: FRIENDS_BUTTON_HEIGHT, + imageURL: FRIENDS_BUTTON_SVG, + subImage: { + x: 0, + y: 0, + width: FRIENDS_BUTTON_SVG_WIDTH, + height: FRIENDS_BUTTON_SVG_HEIGHT + }, + color: FRIENDS_BUTTON_COLOR, + alpha: FRIENDS_BUTTON_ALPHA, + visible: false + }); + + showMe = Settings.getValue(SETTING_USERS_SHOW_ME, ""); + if (DISPLAY_VALUES.indexOf(showMe) === -1) { + showMe = DISPLAY_EVERYONE; + } + + displayControl = new PopUpMenu({ + prompt: DISPLAY_PROMPT, + value: showMe, + values: DISPLAY_VALUES, + displayValues: DISPLAY_DISPLAY_VALUES, + x: 0, + y: viewport.y, + width: WINDOW_WIDTH - 1.5 * WINDOW_MARGIN, + promptWidth: DISPLAY_PROMPT_WIDTH, + lineHeight: windowLineHeight, + textHeight: windowTextHeight, + font: WINDOW_FONT, + promptColor: WINDOW_HEADING_COLOR, + promptAlpha: WINDOW_HEADING_ALPHA, + promptBackgroundColor: WINDOW_BACKGROUND_COLOR, + promptBackgroundAlpha: 0.0, + optionColor: WINDOW_FOREGROUND_COLOR, + optionAlpha: WINDOW_FOREGROUND_ALPHA, + optionBackgroundColor: OPTION_BACKGROUND_COLOR, + optionBackgroundAlpha: OPTION_BACKGROUND_ALPHA, + popupBackgroundColor: DISPLAY_OPTIONS_BACKGROUND_COLOR, + popupBackgroundAlpha: DISPLAY_OPTIONS_BACKGROUND_ALPHA, + buttonColor: MIN_MAX_BUTTON_COLOR, + buttonAlpha: MIN_MAX_BUTTON_ALPHA, + visible: false + }); + + myVisibility = Settings.getValue(SETTING_USERS_VISIBLE_TO, ""); + if (VISIBILITY_VALUES.indexOf(myVisibility) === -1) { + myVisibility = VISIBILITY_FRIENDS; + } + GlobalServices.findableBy = myVisibility; + + visibilityControl = new PopUpMenu({ + prompt: VISIBILITY_PROMPT, + value: myVisibility, + values: VISIBILITY_VALUES, + displayValues: VISIBILITY_DISPLAY_VALUES, + x: 0, + y: viewport.y, + width: WINDOW_WIDTH - 1.5 * WINDOW_MARGIN, + promptWidth: VISIBILITY_PROMPT_WIDTH, + lineHeight: windowLineHeight, + textHeight: windowTextHeight, + font: WINDOW_FONT, + promptColor: WINDOW_HEADING_COLOR, + promptAlpha: WINDOW_HEADING_ALPHA, + promptBackgroundColor: WINDOW_BACKGROUND_COLOR, + promptBackgroundAlpha: 0.0, + optionColor: WINDOW_FOREGROUND_COLOR, + optionAlpha: WINDOW_FOREGROUND_ALPHA, + optionBackgroundColor: OPTION_BACKGROUND_COLOR, + optionBackgroundAlpha: OPTION_BACKGROUND_ALPHA, + popupBackgroundColor: DISPLAY_OPTIONS_BACKGROUND_COLOR, + popupBackgroundAlpha: DISPLAY_OPTIONS_BACKGROUND_ALPHA, + buttonColor: MIN_MAX_BUTTON_COLOR, + buttonAlpha: MIN_MAX_BUTTON_ALPHA, + visible: false + }); + + Controller.mousePressEvent.connect(onMousePressEvent); + Controller.mouseMoveEvent.connect(onMouseMoveEvent); + Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); + + Menu.addMenuItem({ + menuName: MENU_NAME, + menuItemName: MENU_ITEM, + afterItem: MENU_ITEM_AFTER, + isCheckable: true, + isChecked: isVisible + }); + Menu.menuItemEvent.connect(onMenuItemEvent); + + GlobalServices.findableByChanged.connect(onFindableByChanged); + + Script.update.connect(onScriptUpdate); + + pollUsers(); + + // Set minimized at end - setup code does not handle `minimized == false` correctly + setMinimized(isValueTrue(Settings.getValue(SETTING_USERS_WINDOW_MINIMIZED, true))); + } + + function tearDown() { + Menu.removeMenuItem(MENU_NAME, MENU_ITEM); + + Script.clearTimeout(usersTimer); + Overlays.deleteOverlay(windowBorder); + Overlays.deleteOverlay(windowPane); + Overlays.deleteOverlay(windowHeading); + Overlays.deleteOverlay(minimizeButton); + Overlays.deleteOverlay(scrollbarBackground); + Overlays.deleteOverlay(scrollbarBar); + Overlays.deleteOverlay(friendsButton); + displayControl.tearDown(); + visibilityControl.tearDown(); + } + + setUp(); + Script.scriptEnding.connect(tearDown); +}()); + +function cleanup () { + //remove tablet button + button.clicked.disconnect(onClicked); + if (tablet) { + tablet.removeButton(button); + } + if (toolBar) { + toolBar.removeButton(buttonName); + } +} +Script.scriptEnding.connect(cleanup); + }()); // END LOCAL_SCOPE From d4a161041ee5e14c6f816296176057dad3b9eccb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 31 Jan 2017 15:32:47 -0800 Subject: [PATCH 45/52] drop empty parts when splitting script whitelist --- libraries/entities/src/EntityTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5fa86f6745..bf935a7dbe 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -64,7 +64,7 @@ EntityTree::~EntityTree() { } void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) { - _entityScriptSourceWhitelist = entityScriptSourceWhitelist.split(','); + _entityScriptSourceWhitelist = entityScriptSourceWhitelist.split(',', QString::SkipEmptyParts); } From e42853a96c0237a95866c248b81c850956893e25 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 31 Jan 2017 16:05:38 -0800 Subject: [PATCH 46/52] cleanly log failed entity adds, and tell the client to delete his copy --- libraries/entities/src/EntityTree.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5fa86f6745..e2a6d8beb0 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1111,7 +1111,15 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c endUpdate = usecTimestampNow(); _totalUpdates++; } else if (message.getType() == PacketType::EntityAdd) { - if (senderNode->getCanRez() || senderNode->getCanRezTmp()) { + bool failedAdd = !allowed; + if (!allowed) { + qCDebug(entities) << "Filtered entity add. ID:" << entityItemID; + } else if (!senderNode->getCanRez() && !senderNode->getCanRezTmp()) { + failedAdd = true; + qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID() + << "] attempted to add an entity ID:" << entityItemID; + + } else { // this is a new entity... assign a new entityID properties.setCreated(properties.getLastEdited()); properties.setLastEditedBy(senderNode->getUUID()); @@ -1126,7 +1134,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c startLogging = usecTimestampNow(); if (wantEditLogging()) { qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:" - << newEntity->getEntityItemID(); + << newEntity->getEntityItemID(); qCDebug(entities) << " properties:" << properties; } if (wantTerseEditLogging()) { @@ -1136,10 +1144,14 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } endLogging = usecTimestampNow(); + } else { + failedAdd = true; + qCDebug(entities) << "Add entity failed ID:" << entityItemID; } - } else { - qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID() - << "] attempted to add an entity."; + } + if (failedAdd) { // Let client know it failed, so that they don't have an entity that no one else sees. + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } } else { static QString repeatedMessage = From 8f362861eb19a8fd636c1c6d52b252615877f77d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 31 Jan 2017 16:53:25 -0800 Subject: [PATCH 47/52] tablet-ui: button icon images now work with http urls. --- interface/resources/qml/hifi/tablet/TabletButton.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 9ad8d1476c..c6c810d25e 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -75,6 +75,14 @@ Item { source: buttonOutline } + function urlHelper(src) { + if (src.match(/\bhttp/)) { + return src; + } else { + return "../../../" + src; + } + } + Image { id: icon width: 50 @@ -84,7 +92,7 @@ Item { anchors.bottomMargin: 5 anchors.horizontalCenter: parent.horizontalCenter fillMode: Image.Stretch - source: "../../../" + tabletButton.icon + source: tabletButton.urlHelper(tabletButton.icon) } ColorOverlay { @@ -185,7 +193,7 @@ Item { PropertyChanges { target: icon - source: "../../../" + tabletButton.activeIcon + source: tabletButton.urlHelper(tabletButton.activeIcon) } }, State { From cf855391af34d47a44e0ca5e46a7b9da534701c2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 31 Jan 2017 16:54:15 -0800 Subject: [PATCH 48/52] Added jsdocs for all button properties --- .../script-engine/src/TabletScriptingInterface.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index b0b2d00e0f..0b7829c7fb 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -119,7 +119,7 @@ public: * @param msg {object|string} */ Q_INVOKABLE void emitScriptEvent(QVariant msg); - + Q_INVOKABLE bool onHomeScreen(); QObject* getTabletSurface(); @@ -170,14 +170,14 @@ public: /**jsdoc * Returns the current value of this button's properties * @function TabletButtonProxy#getProperties - * @returns {object} + * @returns {ButtonProperties} */ Q_INVOKABLE QVariantMap getProperties() const; /**jsdoc * Replace the values of some of this button's properties * @function TabletButtonProxy#editProperties - * @param properties {object} set of properties to change + * @param {ButtonProperties} properties - set of properties to change */ Q_INVOKABLE void editProperties(QVariantMap properties); @@ -199,4 +199,13 @@ protected: QVariantMap _properties; }; +/**jsdoc + * @typedef TabletButtonProxy.ButtonProperties + * @property {string} text - button caption + * @property {string} icon - url to button icon. (50 x 50) + * @property {string} activeText - button caption when button is active + * @property {string} activeIcon - url to button icon used when button is active. (50 x 50) + * @property {string} isActive - true when button is active. + */ + #endif // hifi_TabletScriptingInterface_h From f03d2988c1ad8f49407f912d6df1ceaaa809d57c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 31 Jan 2017 17:30:45 -0800 Subject: [PATCH 49/52] fix for checkbox name setting --- domain-server/resources/web/settings/js/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 22ce5b3170..b04d55b9eb 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1398,7 +1398,7 @@ function addTableRow(row) { var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length if (isCheckbox) { - $(input).find("input").attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) + input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) } else { input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) } From c147b9bec0672aa9253c406ab0a2f75005906adc Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 31 Jan 2017 18:00:18 -0800 Subject: [PATCH 50/52] Fix for tablets in third-person HMD mode. --- scripts/system/libraries/WebTablet.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 65551b2140..edb637d9a2 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -198,6 +198,11 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { _this.geometryChanged(geometry); }; Window.geometryChanged.connect(this.myGeometryChanged); + + this.myCameraModeChanged = function(newMode) { + _this.cameraModeChanged(newMode); + }; + Camera.modeUpdated.connect(this.myCameraModeChanged); }; WebTablet.prototype.setHomeButtonTexture = function() { @@ -228,6 +233,7 @@ WebTablet.prototype.destroy = function () { Controller.mouseReleaseEvent.disconnect(this.myMouseReleaseEvent); Window.geometryChanged.disconnect(this.myGeometryChanged); + Camera.modeUpdated.disconnect(this.myCameraModeChanged); }; WebTablet.prototype.geometryChanged = function (geometry) { @@ -370,6 +376,19 @@ WebTablet.prototype.mousePressEvent = function (event) { } }; +WebTablet.prototype.cameraModeChanged = function (newMode) { + // reposition the tablet, after a small delay. + // This allows HMD.position to reflect the new camera mode. + var self = this; + Script.setTimeout(function () { + var NO_HANDS = -1; + var tabletProperties = {}; + // compute position, rotation & parentJointIndex of the tablet + self.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); + Entities.editEntity(self.tabletEntityID, tabletProperties); + }, 10); +}; + function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal); if (rayDirectionDotPlaneNormal > 0.00001 || rayDirectionDotPlaneNormal < -0.00001) { From 008a58f9d708e00ce4d8de9de1d9ff70805bb903 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 31 Jan 2017 18:03:48 -0800 Subject: [PATCH 51/52] Only reposition the tablet when camera mode changes, in HMD mode. --- scripts/system/libraries/WebTablet.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index edb637d9a2..52912337a1 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -379,14 +379,16 @@ WebTablet.prototype.mousePressEvent = function (event) { WebTablet.prototype.cameraModeChanged = function (newMode) { // reposition the tablet, after a small delay. // This allows HMD.position to reflect the new camera mode. - var self = this; - Script.setTimeout(function () { - var NO_HANDS = -1; - var tabletProperties = {}; - // compute position, rotation & parentJointIndex of the tablet - self.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); - Entities.editEntity(self.tabletEntityID, tabletProperties); - }, 10); + if (HMD.active) { + var self = this; + Script.setTimeout(function () { + var NO_HANDS = -1; + var tabletProperties = {}; + // compute position, rotation & parentJointIndex of the tablet + self.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); + Entities.editEntity(self.tabletEntityID, tabletProperties); + }, 10); + } }; function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { From 3c04f54e7509b08c36eff7311f16a3f6b636c391 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 31 Jan 2017 18:22:38 -0800 Subject: [PATCH 52/52] more fixes to third person tablet usage. --- scripts/system/libraries/WebTablet.js | 31 +++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 52912337a1..75ca2e514f 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -24,6 +24,7 @@ var CAMERA_MATRIX = -7; var ROT_Y_180 = {x: 0, y: 1, z: 0, w: 0}; var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 }; var INCHES_TO_METERS = 1 / 39.3701; +var NO_HANDS = -1; var TABLET_URL = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx"; @@ -35,18 +36,21 @@ var TABLET_MODEL_PATH = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-wi // * position - position in front of the user // * rotation - rotation of entity so it faces the user. function calcSpawnInfo(hand, height) { - var noHands = -1; var finalPosition; - if (HMD.active && hand !== noHands) { + + var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position; + var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation; + + if (HMD.active && hand !== NO_HANDS) { var handController = getControllerWorldLocation(hand, true); var controllerPosition = handController.position; // compute the angle of the chord with length (height / 2) - var theta = Math.asin(height / (2 * Vec3.distance(HMD.position, controllerPosition))); + var theta = Math.asin(height / (2 * Vec3.distance(headPos, controllerPosition))); // then we can use this angle to rotate the vector between the HMD position and the center of the tablet. // this vector, u, will become our new look at direction. - var d = Vec3.normalize(Vec3.subtract(HMD.position, controllerPosition)); + var d = Vec3.normalize(Vec3.subtract(headPos, controllerPosition)); var w = Vec3.normalize(Vec3.cross(Y_AXIS, d)); var q = Quat.angleAxis(theta * (180 / Math.PI), w); var u = Vec3.multiplyQbyV(q, d); @@ -64,8 +68,8 @@ function calcSpawnInfo(hand, height) { rotation: lookAtRot }; } else { - var front = Quat.getFront(Camera.orientation); - finalPosition = Vec3.sum(Camera.position, Vec3.multiply(0.6, front)); + var front = Quat.getFront(headRot); + finalPosition = Vec3.sum(headPos, Vec3.multiply(0.6, front)); var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, front, {x: 0, y: 1, z: 0}); return { position: finalPosition, @@ -238,7 +242,6 @@ WebTablet.prototype.destroy = function () { WebTablet.prototype.geometryChanged = function (geometry) { if (!HMD.active) { - var NO_HANDS = -1; var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); @@ -294,7 +297,6 @@ WebTablet.prototype.onHmdChanged = function () { Controller.mouseReleaseEvent.connect(this.myMouseReleaseEvent); } - var NO_HANDS = -1; var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); @@ -377,17 +379,14 @@ WebTablet.prototype.mousePressEvent = function (event) { }; WebTablet.prototype.cameraModeChanged = function (newMode) { - // reposition the tablet, after a small delay. + // reposition the tablet. // This allows HMD.position to reflect the new camera mode. if (HMD.active) { var self = this; - Script.setTimeout(function () { - var NO_HANDS = -1; - var tabletProperties = {}; - // compute position, rotation & parentJointIndex of the tablet - self.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); - Entities.editEntity(self.tabletEntityID, tabletProperties); - }, 10); + var tabletProperties = {}; + // compute position, rotation & parentJointIndex of the tablet + self.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties); + Entities.editEntity(self.tabletEntityID, tabletProperties); } };