From 88c0db969c56c70867cce43dbef9190fd1fa8c07 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 3 Mar 2017 17:14:00 +0000 Subject: [PATCH 01/17] added tabletComboBox to tabletFileDialog --- interface/resources/qml/dialogs/TabletFileDialog.qml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/dialogs/TabletFileDialog.qml b/interface/resources/qml/dialogs/TabletFileDialog.qml index 0aef9179e8..b55eca7493 100644 --- a/interface/resources/qml/dialogs/TabletFileDialog.qml +++ b/interface/resources/qml/dialogs/TabletFileDialog.qml @@ -151,11 +151,12 @@ TabletModalWindow { } } - /*TabletComboBox { + TabletComboBox { id: pathSelector - anchors { + z: 10 + anchors { top: parent.top - topMargin: hifi.dimensions.contentMargin.y + topMargin: (fileDialogItem.hasTitle ? (fileDialogItem.frameMarginTop + hifi.dimensions.modalDialogMargin.y) : hifi.dimension.modalDialogMargin.y) left: navControls.right leftMargin: hifi.dimensions.contentSpacing.x right: parent.right @@ -219,7 +220,7 @@ TabletModalWindow { fileTableView.forceActiveFocus(); } } - }*/ + } QtObject { id: d From d85cb645b0301338f5cedad9b547772d8e4b6890 Mon Sep 17 00:00:00 2001 From: Menithal Date: Sat, 4 Mar 2017 11:42:43 +0200 Subject: [PATCH 02/17] Changed limit logic, default limit is now 0 Clones now have a named based on the original entity id Limit is now calculated from the source instance, instead of just clone name to avoid a single box being calculated as something else Default limit is now 0, which disables limit --- .../system/controllers/handControllerGrab.js | 76 +++++++++---------- scripts/system/html/js/entityProperties.js | 6 +- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2e1d538db9..26178773e9 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -274,24 +274,24 @@ CONTROLLER_STATE_MACHINE[STATE_OVERLAY_LASER_TOUCHING] = CONTROLLER_STATE_MACHIN // Object assign polyfill if (typeof Object.assign != 'function') { - Object.assign = function(target, varArgs) { - 'use strict'; - if (target == null) { - throw new TypeError('Cannot convert undefined or null to object'); - } - var to = Object(target); - for (var index = 1; index < arguments.length; index++) { - var nextSource = arguments[index]; - if (nextSource != null) { - for (var nextKey in nextSource) { - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } + Object.assign = function(target, varArgs) { + 'use strict'; + if (target == null) { + throw new TypeError('Cannot convert undefined or null to object'); } - } - } - return to; - }; + var to = Object(target); + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + if (nextSource != null) { + for (var nextKey in nextSource) { + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }; } function distanceBetweenPointAndEntityBoundingBox(point, entityProps) { @@ -1524,16 +1524,16 @@ function MyController(hand) { return true; }; this.entityIsCloneable = function(entityID) { - var entityProps = entityPropertiesCache.getGrabbableProps(entityID); - var props = entityPropertiesCache.getProps(entityID); - if (!props) { - return false; - } + var entityProps = entityPropertiesCache.getGrabbableProps(entityID); + var props = entityPropertiesCache.getProps(entityID); + if (!props) { + return false; + } - if (entityProps.hasOwnProperty("cloneable")) { - return entityProps.cloneable; - } - return false; + if (entityProps.hasOwnProperty("cloneable")) { + return entityProps.cloneable; + } + return false; } this.entityIsGrabbable = function(entityID) { var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); @@ -2229,7 +2229,7 @@ function MyController(hand) { // Can't set state of other controller to STATE_DISTANCE_HOLDING because then either: // (a) The entity would jump to line up with the formerly rotating controller's orientation, or // (b) The grab beam would need an orientation offset to the controller's true orientation. - // Neither of these options is good, so instead set STATE_SEARCHING and subsequently let the formerly distance + // Neither of these options is good, so instead set STATE_SEARCHING and subsequently let the formerly distance // rotating controller start distance holding the entity if it happens to be pointing at the entity. } return; @@ -2601,31 +2601,29 @@ function MyController(hand) { var userData = JSON.parse(grabbedProperties.userData); var grabInfo = userData.grabbableKey; if (grabInfo && grabInfo.cloneable) { - // Check if - var worldEntities = Entities.findEntitiesInBox(Vec3.subtract(MyAvatar.position, {x:25,y:25, z:25}), {x:50, y: 50, z: 50}) + var worldEntities = Entities.findEntities(MyAvatar.position, 50); var count = 0; worldEntities.forEach(function(item) { var item = Entities.getEntityProperties(item, ["name"]); - if (item.name === grabbedProperties.name) { + if (item.name.indexOf('-clone-' + grabbedProperties.id) !== -1) { count++; } }) + + var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; + if (count >= limit && limit !== 0) { + delete limit; + return; + } + var cloneableProps = Entities.getEntityProperties(grabbedProperties.id); + cloneableProps.name = cloneableProps.name + '-clone-' + grabbedProperties.id; var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300; - var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 10; var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false; var cUserData = Object.assign({}, userData); var cProperties = Object.assign({}, cloneableProps); isClone = true; - if (count > limit) { - delete cloneableProps; - delete lifetime; - delete cUserData; - delete cProperties; - return; - } - delete cUserData.grabbableKey.cloneLifetime; delete cUserData.grabbableKey.cloneable; delete cUserData.grabbableKey.cloneDynamic; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 3280e1f196..66abcaa67a 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -879,7 +879,7 @@ function loaded() { elCloneable.checked = false; elCloneableDynamic.checked = false; elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; - elCloneableLimit.value = 10; + elCloneableLimit.value = 0; elCloneableLifetime.value = 300; var parsedUserData = {} @@ -899,8 +899,6 @@ function loaded() { if ("cloneable" in parsedUserData["grabbableKey"]) { elCloneable.checked = parsedUserData["grabbableKey"].cloneable; elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; - elCloneableLimit.value = elCloneable.checked ? 10: 0; - elCloneableLifetime.value = elCloneable.checked ? 300: 0; elCloneableDynamic.checked = parsedUserData["grabbableKey"].cloneDynamic ? parsedUserData["grabbableKey"].cloneDynamic : properties.dynamic; elDynamic.checked = elCloneable.checked ? false: properties.dynamic; if (elCloneable.checked) { @@ -908,7 +906,7 @@ function loaded() { elCloneableLifetime.value = parsedUserData["grabbableKey"].cloneLifetime ? parsedUserData["grabbableKey"].cloneLifetime : 300; } if ("cloneLimit" in parsedUserData["grabbableKey"]) { - elCloneableLimit.value = parsedUserData["grabbableKey"].cloneLimit ? parsedUserData["grabbableKey"].cloneLimit : 10; + elCloneableLimit.value = parsedUserData["grabbableKey"].cloneLimit ? parsedUserData["grabbableKey"].cloneLimit : 0; } } } From 6d9e7de6dcb8d1555332ca2f5314a55827f5c75a Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Mon, 6 Mar 2017 21:45:55 +0100 Subject: [PATCH 03/17] Implemented AssetBrowser in Tablet view --- CMakeLists.txt | 11 + interface/CMakeLists.txt | 11 + .../qml/dialogs/TabletCustomQueryDialog.qml | 339 ++++++++++ .../qml/hifi/dialogs/TabletAssetServer.qml | 614 ++++++++++++++++++ .../resources/qml/hifi/tablet/TabletRoot.qml | 3 + .../qml/styles-uit/HifiConstants.qml | 1 + interface/src/Application.cpp | 19 +- interface/src/main.cpp | 2 + .../AssetMappingsScriptingInterface.h | 8 +- interface/src/ui/overlays/Web3DOverlay.cpp | 7 + 10 files changed, 1011 insertions(+), 4 deletions(-) create mode 100644 interface/resources/qml/dialogs/TabletCustomQueryDialog.qml create mode 100644 interface/resources/qml/hifi/dialogs/TabletAssetServer.qml diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ab7e55343..d7ea297637 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,6 +201,17 @@ set(HF_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(MACRO_DIR "${HF_CMAKE_DIR}/macros") set(EXTERNAL_PROJECT_DIR "${HF_CMAKE_DIR}/externals") +file(GLOB_RECURSE JS_SRC scripts/*.js) +add_custom_target(js SOURCES ${JS_SRC}) + +if (UNIX) + install( + DIRECTORY "${CMAKE_SOURCE_DIR}/scripts" + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/interface + COMPONENT ${CLIENT_COMPONENT} + ) +endif() + file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake") foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS}) include(${CUSTOM_MACRO}) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 868a2cf933..0ae7539448 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -54,6 +54,17 @@ find_package( WebChannel WebSockets ) +file(GLOB_RECURSE QML_SRC resources/qml/*.qml resources/qml/*.js) +add_custom_target(qml SOURCES ${QML_SRC}) + +if (UNIX) + install( + DIRECTORY "${CMAKE_SOURCE_DIR}/interface/resources/qml" + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/resources + COMPONENT ${CLIENT_COMPONENT} + ) +endif() + # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) source_group("UI Files" FILES ${QT_UI_FILES}) diff --git a/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml new file mode 100644 index 0000000000..6e5bc50d32 --- /dev/null +++ b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml @@ -0,0 +1,339 @@ +// +// TabletCustomQueryDialog.qml +// +// Created by Vlad Stelmahovsky on 3/27/17 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Dialogs 1.2 as OriginalDialogs + +import "../controls-uit" as ControlsUIT +import "../styles-uit" +import "../windows" + +TabletModalWindow { + id: root; + HifiConstants { id: hifi; } + + y: hifi.dimensions.tabletMenuHeader + + title: "" + visible: true; + property bool keyboardOverride: true + + signal selected(var result); + signal canceled(); + + property int icon: hifi.icons.none; + property string iconText: ""; + property int iconSize: 35; + onIconChanged: updateIcon(); + + property var textInput; + property var comboBox; + property var checkBox; + onTextInputChanged: { + if (textInput && textInput.text !== undefined) { + textField.text = textInput.text; + } + } + onComboBoxChanged: { + if (comboBox && comboBox.index !== undefined) { + comboBoxField.currentIndex = comboBox.index; + } + } + onCheckBoxChanged: { + if (checkBox && checkBox.checked !== undefined) { + checkBoxField.checked = checkBox.checked; + } + } + + property bool keyboardEnabled: false + property bool keyboardRaised: false + property bool punctuationMode: false + onKeyboardRaisedChanged: d.resize(); + + property var warning: ""; + property var result; + + property var implicitCheckState: null; + + property int titleWidth: 0; + onTitleWidthChanged: d.resize(); + + MouseArea { + width: parent.width + height: parent.height + } + + function updateIcon() { + if (!root) { + return; + } + iconText = hifi.glyphForIcon(root.icon); + } + + function updateCheckbox() { + if (checkBox.disableForItems) { + var currentItemInDisableList = false; + for (var i in checkBox.disableForItems) { + if (comboBoxField.currentIndex === checkBox.disableForItems[i]) { + currentItemInDisableList = true; + break; + } + } + + if (currentItemInDisableList) { + checkBoxField.enabled = false; + if (checkBox.checkStateOnDisable !== null && checkBox.checkStateOnDisable !== undefined) { + root.implicitCheckState = checkBoxField.checked; + checkBoxField.checked = checkBox.checkStateOnDisable; + } + root.warning = checkBox.warningOnDisable; + } else { + checkBoxField.enabled = true; + if (root.implicitCheckState !== null) { + checkBoxField.checked = root.implicitCheckState; + root.implicitCheckState = null; + } + root.warning = ""; + } + } + } + + TabletModalFrame { + id: modalWindowItem + width: parent.width - 12 + height: 240 + anchors { + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter + } + + QtObject { + id: d; + readonly property int minWidth: 470 + readonly property int maxWidth: 470 + readonly property int minHeight: 240 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, 470); + var targetHeight = (textField.visible ? textField.controlHeight + hifi.dimensions.contentSpacing.y : 0) + + (extraInputs.visible ? extraInputs.height + hifi.dimensions.contentSpacing.y : 0) + + (buttons.height + 3 * hifi.dimensions.contentSpacing.y) + + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + hifi.dimensions.contentSpacing.y) : 0); + + root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth); + root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? + d.maxHeight : targetHeight); + if (checkBoxField.visible && comboBoxField.visible) { + checkBoxField.width = extraInputs.width / 2; + comboBoxField.width = extraInputs.width / 2; + } else if (!checkBoxField.visible && comboBoxField.visible) { + comboBoxField.width = extraInputs.width; + } + } + } +// Item { +// anchors { +// // top: parent.top; +// // bottom: extraInputs.visible ? extraInputs.top : buttons.top; +// left: parent.left; +// right: parent.right; +// topMargin: 20 +// } + + // FIXME make a text field type that can be bound to a history for autocompletion + ControlsUIT.TextField { + id: textField; + label: root.textInput.label; + focus: root.textInput ? true : false; + visible: root.textInput ? true : false; + anchors { + top: parent.top + left: parent.left; + right: parent.right; + leftMargin: 5; rightMargin: 5; + topMargin: textField.controlHeight - textField.height + 5 + } + } + + ControlsUIT.Keyboard { + id: keyboard + raised: keyboardEnabled && keyboardRaised + numeric: punctuationMode + anchors { + left: parent.left + right: parent.right + leftMargin: 5; rightMargin: 5; + top: textField.bottom + topMargin: raised ? hifi.dimensions.contentSpacing.y : 0 + } + } + // } + + Row { + id: extraInputs; + visible: Boolean(root.checkBox || root.comboBox); + anchors { + left: parent.left; + right: parent.right; + leftMargin: 5; rightMargin: 5; + top: keyboard.bottom; + topMargin: hifi.dimensions.contentSpacing.y + 20; + } + height: comboBoxField.controlHeight; + onHeightChanged: d.resize(); + onWidthChanged: d.resize(); + + ControlsUIT.CheckBox { + id: checkBoxField; + text: root.checkBox.label; + focus: Boolean(root.checkBox); + visible: Boolean(root.checkBox); + // anchors { + // left: parent.left; + // bottom: parent.bottom; + // leftMargin: 6; // Magic number to align with warning icon + // bottomMargin: 6; + // } + } + + ControlsUIT.TabletComboBox { + id: comboBoxField; + label: root.comboBox.label; + focus: Boolean(root.comboBox); + visible: Boolean(root.comboBox); + // anchors { + // right: parent.right; + // bottom: parent.bottom; + // } + model: root.comboBox ? root.comboBox.items : []; + onAccepted: { + updateCheckbox(); + focus = true; + } + } + } + + Row { + id: buttons; + focus: true; + spacing: hifi.dimensions.contentSpacing.x; + layoutDirection: Qt.RightToLeft; + onHeightChanged: d.resize(); + onWidthChanged: { + d.resize(); + resizeWarningText(); + } + + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + bottomMargin: hifi.dimensions.contentSpacing.y; + leftMargin: 5; rightMargin: 5; + } + + function resizeWarningText() { + var rowWidth = buttons.width; + var buttonsWidth = acceptButton.width + cancelButton.width + hifi.dimensions.contentSpacing.x * 2; + var warningIconWidth = warningIcon.width + hifi.dimensions.contentSpacing.x; + warningText.width = rowWidth - buttonsWidth - warningIconWidth; + } + + ControlsUIT.Button { + id: cancelButton; + action: cancelAction; + } + + ControlsUIT.Button { + id: acceptButton; + action: acceptAction; + } + + Text { + id: warningText; + visible: Boolean(root.warning); + text: root.warning; + wrapMode: Text.WordWrap; + font.italic: true; + maximumLineCount: 2; + } + + HiFiGlyphs { + id: warningIcon; + visible: Boolean(root.warning); + text: hifi.glyphs.alert; + size: hifi.dimensions.controlLineHeight; + } + } +// }//column + Action { + id: cancelAction; + text: qsTr("Cancel"); + shortcut: Qt.Key_Escape; + onTriggered: { + root.result = null; + root.canceled(); + root.destroy(); + } + } + + Action { + id: acceptAction; + text: qsTr("Add"); + shortcut: Qt.Key_Return; + onTriggered: { + var result = {}; + if (textInput) { + result.textInput = textField.text; + } + if (comboBox) { + result.comboBox = comboBoxField.currentIndex; + result.comboBoxText = comboBoxField.currentText; + } + if (checkBox) { + result.checkBox = checkBoxField.enabled ? checkBoxField.checked : null; + } + root.result = JSON.stringify(result); + root.selected(root.result); + root.destroy(); + } + } + } + + Keys.onPressed: { + if (!visible) { + return; + } + + switch (event.key) { + case Qt.Key_Escape: + case Qt.Key_Back: + cancelAction.trigger(); + event.accepted = true; + break; + + case Qt.Key_Return: + case Qt.Key_Enter: + acceptAction.trigger(); + event.accepted = true; + break; + } + } + + Component.onCompleted: { + keyboardEnabled = HMD.active; + updateIcon(); + updateCheckbox(); + d.resize(); + textField.forceActiveFocus(); + } +} diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml new file mode 100644 index 0000000000..93e18c7aba --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -0,0 +1,614 @@ +// +// TabletAssetServer.qml +// +// Created by Vlad Stelmahovsky on 3/3/17 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Dialogs 1.2 as OriginalDialogs +import Qt.labs.settings 1.0 + +import "../../styles-uit" +import "../../controls-uit" as HifiControls +import "../../windows" + +Rectangle { + id: root + objectName: "AssetServer" + + property var eventBridge; + signal sendToScript(var message); + property bool isHMD: false + + color: hifi.colors.baseGray + + property int colorScheme: hifi.colorSchemes.dark + + HifiConstants { id: hifi } + + property var scripts: ScriptDiscoveryService; + property var assetProxyModel: Assets.proxyModel; + property var assetMappingsModel: Assets.mappingModel; + property var currentDirectory; + + Settings { + category: "Overlay.AssetServer" + property alias directory: root.currentDirectory + } + + Component.onCompleted: { + isHMD = HMD.active; + ApplicationInterface.uploadRequest.connect(uploadClicked); + assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError); + reload(); + } + + function doDeleteFile(path) { + console.log("Deleting " + path); + + Assets.deleteMappings(path, function(err) { + if (err) { + console.log("Asset browser - error deleting path: ", path, err); + + box = errorMessageBox("There was an error deleting:\n" + path + "\n" + err); + box.selected.connect(reload); + } else { + console.log("Asset browser - finished deleting path: ", path); + reload(); + } + }); + + } + + function doRenameFile(oldPath, newPath) { + + if (newPath[0] !== "/") { + newPath = "/" + newPath; + } + + if (oldPath[oldPath.length - 1] === '/' && newPath[newPath.length - 1] != '/') { + // this is a folder rename but the user neglected to add a trailing slash when providing a new path + newPath = newPath + "/"; + } + + if (Assets.isKnownFolder(newPath)) { + box = errorMessageBox("Cannot overwrite existing directory."); + box.selected.connect(reload); + } + + console.log("Asset browser - renaming " + oldPath + " to " + newPath); + + Assets.renameMapping(oldPath, newPath, function(err) { + if (err) { + console.log("Asset browser - error renaming: ", oldPath, "=>", newPath, " - error ", err); + box = errorMessageBox("There was an error renaming:\n" + oldPath + " to " + newPath + "\n" + err); + box.selected.connect(reload); + } else { + console.log("Asset browser - finished rename: ", oldPath, "=>", newPath); + } + + reload(); + }); + } + + function fileExists(path) { + return Assets.isKnownMapping(path); + } + + function askForOverwrite(path, callback) { + var object = tabletRoot.messageBox({ + icon: hifi.icons.question, + buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, + defaultButton: OriginalDialogs.StandardButton.No, + title: "Overwrite File", + text: path + "\n" + "This file already exists. Do you want to overwrite it?" + }); + object.selected.connect(function(button) { + if (button === OriginalDialogs.StandardButton.Yes) { + callback(); + } + }); + } + + function canAddToWorld(path) { + var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i]; + + return supportedExtensions.reduce(function(total, current) { + return total | new RegExp(current).test(path); + }, false); + } + + function clear() { + Assets.mappingModel.clear(); + } + + function reload() { + Assets.mappingModel.refresh(); + treeView.selection.clear(); + } + + function handleGetMappingsError(errorString) { + errorMessageBox( + "There was a problem retreiving the list of assets from your Asset Server.\n" + + errorString + ); + } + + function addToWorld() { + var defaultURL = assetProxyModel.data(treeView.selection.currentIndex, 0x103); + + if (!defaultURL || !canAddToWorld(defaultURL)) { + return; + } + + var SHAPE_TYPE_NONE = 0; + var SHAPE_TYPE_SIMPLE_HULL = 1; + var SHAPE_TYPE_SIMPLE_COMPOUND = 2; + var SHAPE_TYPE_STATIC_MESH = 3; + + var SHAPE_TYPES = []; + SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; + SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; + SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; + SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; + + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; + var DYNAMIC_DEFAULT = false; + var prompt = tabletRoot.customInputDialog({ + textInput: { + label: "Model URL", + text: defaultURL + }, + comboBox: { + label: "Automatic Collisions", + index: SHAPE_TYPE_DEFAULT, + items: SHAPE_TYPES + }, + checkBox: { + label: "Dynamic", + checked: DYNAMIC_DEFAULT, + disableForItems: [ + SHAPE_TYPE_STATIC_MESH + ], + checkStateOnDisable: false, + warningOnDisable: "Models with automatic collisions set to 'Exact' cannot be dynamic" + } + }); + + prompt.selected.connect(function (jsonResult) { + if (jsonResult) { + var result = JSON.parse(jsonResult); + var url = result.textInput.trim(); + var shapeType; + switch (result.comboBox) { + case SHAPE_TYPE_SIMPLE_HULL: + shapeType = "simple-hull"; + break; + case SHAPE_TYPE_SIMPLE_COMPOUND: + shapeType = "simple-compound"; + break; + case SHAPE_TYPE_STATIC_MESH: + shapeType = "static-mesh"; + break; + default: + shapeType = "none"; + } + + var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT; + if (shapeType === "static-mesh" && dynamic) { + // The prompt should prevent this case + print("Error: model cannot be both static mesh and dynamic. This should never happen."); + } else if (url) { + var name = assetProxyModel.data(treeView.selection.currentIndex); + var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation))); + var gravity; + if (dynamic) { + // Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a + // different scripting engine from QTScript. + gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10); + } else { + gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); + } + + print("Asset browser - adding asset " + url + " (" + name + ") to world."); + + // Entities.addEntity doesn't work from QML, so we use this. + Entities.addModelEntity(name, url, shapeType, dynamic, addPosition, gravity); + } + } + }); + } + + function copyURLToClipboard(index) { + if (!index) { + index = treeView.selection.currentIndex; + } + + var url = assetProxyModel.data(treeView.selection.currentIndex, 0x103); + if (!url) { + return; + } + Window.copyToClipboard(url); + } + + function renameEl(index, data) { + if (!index) { + return false; + } + + var path = assetProxyModel.data(index, 0x100); + if (!path) { + return false; + } + + var destinationPath = path.split('/'); + destinationPath[destinationPath.length - (path[path.length - 1] === '/' ? 2 : 1)] = data; + destinationPath = destinationPath.join('/').trim(); + + if (path === destinationPath) { + return; + } + if (!fileExists(destinationPath)) { + doRenameFile(path, destinationPath); + } + } + function renameFile(index) { + if (!index) { + index = treeView.selection.currentIndex; + } + + var path = assetProxyModel.data(index, 0x100); + if (!path) { + return; + } + + var object = tabletRoot.inputDialog({ + label: "Enter new path:", + current: path, + placeholderText: "Enter path here" + }); + object.selected.connect(function(destinationPath) { + destinationPath = destinationPath.trim(); + + if (path === destinationPath) { + return; + } + if (fileExists(destinationPath)) { + askForOverwrite(destinationPath, function() { + doRenameFile(path, destinationPath); + }); + } else { + doRenameFile(path, destinationPath); + } + }); + } + function deleteFile(index) { + if (!index) { + index = treeView.selection.currentIndex; + } + var path = assetProxyModel.data(index, 0x100); + if (!path) { + return; + } + + var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101); + var typeString = isFolder ? 'folder' : 'file'; + + var object = tabletRoot.messageBox({ + icon: hifi.icons.question, + buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No, + defaultButton: OriginalDialogs.StandardButton.Yes, + title: "Delete", + text: "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?" + }); + object.selected.connect(function(button) { + if (button === OriginalDialogs.StandardButton.Yes) { + doDeleteFile(path); + } + }); + } + + Timer { + id: doUploadTimer + property var url + property bool isConnected: false + interval: 5 + repeat: false + running: false + } + + property bool uploadOpen: false; + Timer { + id: timer + } + function uploadClicked(fileUrl) { + if (uploadOpen) { + return; + } + uploadOpen = true; + + function doUpload(url, dropping) { + var fileUrl = fileDialogHelper.urlToPath(url); + + var path = assetProxyModel.data(treeView.selection.currentIndex, 0x100); + var directory = path ? path.slice(0, path.lastIndexOf('/') + 1) : "/"; + var filename = fileUrl.slice(fileUrl.lastIndexOf('/') + 1); + + Assets.uploadFile(fileUrl, directory + filename, + function() { + // Upload started + uploadSpinner.visible = true; + uploadButton.enabled = false; + uploadProgressLabel.text = "In progress..."; + }, + function(err, path) { + print(err, path); + if (err === "") { + uploadProgressLabel.text = "Upload Complete"; + timer.interval = 1000; + timer.repeat = false; + timer.triggered.connect(function() { + uploadSpinner.visible = false; + uploadButton.enabled = true; + uploadOpen = false; + }); + timer.start(); + console.log("Asset Browser - finished uploading: ", fileUrl); + reload(); + } else { + uploadSpinner.visible = false; + uploadButton.enabled = true; + uploadOpen = false; + + if (err !== -1) { + console.log("Asset Browser - error uploading: ", fileUrl, " - error ", err); + var box = errorMessageBox("There was an error uploading:\n" + fileUrl + "\n" + err); + box.selected.connect(reload); + } + } + }, dropping); + } + + function initiateUpload(url) { + doUpload(doUploadTimer.url, false); + } + + if (fileUrl) { + doUpload(fileUrl, true); + } else { + var browser = tabletRoot.fileDialog({ + selectDirectory: false, + dir: currentDirectory + }); + + browser.canceled.connect(function() { + uploadOpen = false; + }); + + browser.selectedFile.connect(function(url) { + currentDirectory = browser.dir; + + // Initiate upload from a timer so that file browser dialog can close beforehand. + doUploadTimer.url = url; + if (!doUploadTimer.isConnected) { + doUploadTimer.triggered.connect(function() { initiateUpload(); }); + doUploadTimer.isConnected = true; + } + doUploadTimer.start(); + }); + } + } + + function errorMessageBox(message) { + return tabletRoot.messageBox({ + icon: hifi.icons.warning, + defaultButton: OriginalDialogs.StandardButton.Ok, + title: "Error", + text: message + }); + } + + Column { + width: parent.width + y: hifi.dimensions.tabletMenuHeader //-bgNavBar + spacing: 10 + + HifiControls.TabletContentSection { + id: assetDirectory + name: "Asset Directory" + isFirst: true + + HifiControls.VerticalSpacer {} + + Row { + id: buttonRow + width: parent.width + height: 30 + spacing: hifi.dimensions.contentSpacing.x + + HifiControls.GlyphButton { + glyph: hifi.glyphs.reload + color: hifi.buttons.black + colorScheme: root.colorScheme + width: hifi.dimensions.controlLineHeight + + onClicked: root.reload() + } + + HifiControls.Button { + text: "Add To World" + color: hifi.buttons.black + colorScheme: root.colorScheme + width: 120 + + enabled: canAddToWorld(assetProxyModel.data(treeView.selection.currentIndex, 0x100)) + + onClicked: root.addToWorld() + } + + HifiControls.Button { + text: "Rename" + color: hifi.buttons.black + colorScheme: root.colorScheme + width: 80 + + onClicked: root.renameFile() + enabled: treeView.selection.hasSelection + } + + HifiControls.Button { + id: deleteButton + + text: "Delete" + color: hifi.buttons.red + colorScheme: root.colorScheme + width: 80 + + onClicked: root.deleteFile() + enabled: treeView.selection.hasSelection + } + } + + Menu { + id: contextMenu + title: "Edit" + property var url: "" + property var currentIndex: null + + MenuItem { + text: "Copy URL" + onTriggered: { + copyURLToClipboard(contextMenu.currentIndex); + } + } + + MenuItem { + text: "Rename" + onTriggered: { + renameFile(contextMenu.currentIndex); + } + } + + MenuItem { + text: "Delete" + onTriggered: { + deleteFile(contextMenu.currentIndex); + } + } + } + + } + HifiControls.Tree { + id: treeView + height: 430 + anchors.leftMargin: hifi.dimensions.contentMargin.x + 2 // Extra for border + anchors.rightMargin: hifi.dimensions.contentMargin.x + 2 // Extra for border + anchors.left: parent.left + anchors.right: parent.right + + treeModel: assetProxyModel + canEdit: true + colorScheme: root.colorScheme + + modifyEl: renameEl + + MouseArea { + propagateComposedEvents: true + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: { + if (!HMD.active) { // Popup only displays properly on desktop + var index = treeView.indexAt(mouse.x, mouse.y); + treeView.selection.setCurrentIndex(index, 0x0002); + contextMenu.currentIndex = index; + contextMenu.popup(); + } + } + } + } + + + HifiControls.TabletContentSection { + id: uploadSection + name: "Upload A File" + spacing: hifi.dimensions.contentSpacing.y + //anchors.bottom: parent.bottom + height: 65 + anchors.left: parent.left + anchors.right: parent.right + + Item { + height: parent.height + width: parent.width + HifiControls.Button { + id: uploadButton + anchors.right: parent.right + + text: "Choose File" + color: hifi.buttons.blue + colorScheme: root.colorScheme + height: 30 + width: 155 + + onClicked: uploadClickedTimer.running = true + + // For some reason trigginer an API that enters + // an internal event loop directly from the button clicked + // trigger below causes the appliction to behave oddly. + // Most likely because the button onClicked handling is never + // completed until the function returns. + // FIXME find a better way of handling the input dialogs that + // doesn't trigger this. + Timer { + id: uploadClickedTimer + interval: 5 + repeat: false + running: false + onTriggered: uploadClicked(); + } + } + + Item { + id: uploadSpinner + visible: false + anchors.top: parent.top + anchors.left: parent.left + width: 40 + height: 32 + + Image { + id: image + width: 24 + height: 24 + source: "../../../images/Loading-Outer-Ring.png" + RotationAnimation on rotation { + loops: Animation.Infinite + from: 0 + to: 360 + duration: 2000 + } + } + Image { + width: 24 + height: 24 + source: "../../../images/Loading-Inner-H.png" + } + HifiControls.Label { + id: uploadProgressLabel + anchors.left: image.right + anchors.leftMargin: 10 + anchors.verticalCenter: image.verticalCenter + text: "In progress..." + colorScheme: root.colorScheme + } + } + } + } + } +} + diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 8eaddfb601..99c9351993 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 import Hifi 1.0 import QtQuick.Controls 1.4 import "../../dialogs" + Item { id: tabletRoot objectName: "tabletRoot" @@ -29,7 +30,9 @@ Item { return openMessage; } + Component { id: customInputDialogBuilder; TabletCustomQueryDialog { } } function customInputDialog(properties) { + return customInputDialogBuilder.createObject(tabletRoot, properties); } Component { id: fileDialogBuilder; TabletFileDialog { } } diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 031e80283e..38534d4243 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -159,6 +159,7 @@ Item { readonly property vector2d menuPadding: Qt.vector2d(14, 102) readonly property real scrollbarBackgroundWidth: 18 readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2 + readonly property real tabletMenuHeader: 90 } Item { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index be385a0cac..af5a93a905 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -499,6 +499,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) DependencyManager::set(); @@ -1972,7 +1973,7 @@ void Application::initializeUi() { rootContext->setContextProperty("Quat", new Quat()); rootContext->setContextProperty("Vec3", new Vec3()); rootContext->setContextProperty("Uuid", new ScriptUUID()); - rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface()); + rootContext->setContextProperty("Assets", DependencyManager::get().data()); rootContext->setContextProperty("AvatarList", DependencyManager::get().data()); rootContext->setContextProperty("Users", DependencyManager::get().data()); @@ -5838,7 +5839,21 @@ void Application::showAssetServerWidget(QString filePath) { emit uploadRequest(filePath); } }; - DependencyManager::get()->show(url, "AssetServer", startUpload); + auto tabletScriptingInterface = DependencyManager::get(); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + + if (tablet->getToolbarMode()) { + DependencyManager::get()->show(url, "AssetServer", startUpload); + } else { + QQuickItem* tabletRoot = tablet->getTabletRoot(); + if (!tabletRoot && !isHMDMode()) { + DependencyManager::get()->show(url, "AssetServer", startUpload); + } else { + static const QUrl url("../../hifi/dialogs/TabletAssetServer.qml"); + tablet->pushOntoStack(url); + } + } + startUpload(nullptr, nullptr); } diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 39b37e3d19..b64541a9f4 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -29,6 +29,7 @@ #include "UserActivityLogger.h" #include "MainWindow.h" #include +#include #ifdef HAS_BUGSPLAT #include @@ -194,6 +195,7 @@ int main(int argc, const char* argv[]) { { Application app(argc, const_cast(argv), startupTime, runServer, serverContentPathOptionValue); + QNetworkProxyFactory::setUseSystemConfiguration(true); // If we failed the OpenGLVersion check, log it. if (override) { auto accountManager = DependencyManager::get(); diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.h b/interface/src/scripting/AssetMappingsScriptingInterface.h index 459f01b512..b7fcea2491 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.h +++ b/interface/src/scripting/AssetMappingsScriptingInterface.h @@ -20,6 +20,8 @@ #include #include +#include "DependencyManager.h" + class AssetMappingModel : public QStandardItemModel { Q_OBJECT @@ -39,10 +41,12 @@ private: QHash _pathToItemMap; }; -Q_DECLARE_METATYPE(AssetMappingModel*); +Q_DECLARE_METATYPE(AssetMappingModel*) -class AssetMappingsScriptingInterface : public QObject { +class AssetMappingsScriptingInterface : public QObject, public Dependency { Q_OBJECT + SINGLETON_DEPENDENCY + Q_PROPERTY(AssetMappingModel* mappingModel READ getAssetMappingModel CONSTANT) Q_PROPERTY(QAbstractProxyModel* proxyModel READ getProxyModel CONSTANT) public: diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index b817960f0d..996615d234 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -37,9 +37,11 @@ #include #include "scripting/AccountScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" +#include "scripting/AssetMappingsScriptingInterface.h" #include #include #include "FileDialogHelper.h" +#include "avatar/AvatarManager.h" static const float DPI = 30.47f; @@ -163,6 +165,10 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("UserActivityLogger", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("Preferences", DependencyManager::get().data()); + _webSurface->getRootContext()->setContextProperty("Vec3", new Vec3()); + _webSurface->getRootContext()->setContextProperty("Quat", new Quat()); + _webSurface->getRootContext()->setContextProperty("MyAvatar", DependencyManager::get()->getMyAvatar().get()); + _webSurface->getRootContext()->setContextProperty("Entities", DependencyManager::get().data()); if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") { auto tabletScriptingInterface = DependencyManager::get(); @@ -173,6 +179,7 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("fileDialogHelper", new FileDialogHelper()); _webSurface->getRootContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); + _webSurface->getRootContext()->setContextProperty("Assets", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("pathToFonts", "../../../"); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data()); From 0a36ebc5a0a3e3c36e069a57904dd78a8ef4f6b4 Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Mon, 6 Mar 2017 22:11:46 +0100 Subject: [PATCH 04/17] Remove non relevant changes --- CMakeLists.txt | 11 ----------- interface/CMakeLists.txt | 11 ----------- interface/src/main.cpp | 2 -- 3 files changed, 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7ea297637..1ab7e55343 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,17 +201,6 @@ set(HF_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(MACRO_DIR "${HF_CMAKE_DIR}/macros") set(EXTERNAL_PROJECT_DIR "${HF_CMAKE_DIR}/externals") -file(GLOB_RECURSE JS_SRC scripts/*.js) -add_custom_target(js SOURCES ${JS_SRC}) - -if (UNIX) - install( - DIRECTORY "${CMAKE_SOURCE_DIR}/scripts" - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/interface - COMPONENT ${CLIENT_COMPONENT} - ) -endif() - file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake") foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS}) include(${CUSTOM_MACRO}) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0ae7539448..868a2cf933 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -54,17 +54,6 @@ find_package( WebChannel WebSockets ) -file(GLOB_RECURSE QML_SRC resources/qml/*.qml resources/qml/*.js) -add_custom_target(qml SOURCES ${QML_SRC}) - -if (UNIX) - install( - DIRECTORY "${CMAKE_SOURCE_DIR}/interface/resources/qml" - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/resources - COMPONENT ${CLIENT_COMPONENT} - ) -endif() - # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) source_group("UI Files" FILES ${QT_UI_FILES}) diff --git a/interface/src/main.cpp b/interface/src/main.cpp index b64541a9f4..39b37e3d19 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -29,7 +29,6 @@ #include "UserActivityLogger.h" #include "MainWindow.h" #include -#include #ifdef HAS_BUGSPLAT #include @@ -195,7 +194,6 @@ int main(int argc, const char* argv[]) { { Application app(argc, const_cast(argv), startupTime, runServer, serverContentPathOptionValue); - QNetworkProxyFactory::setUseSystemConfiguration(true); // If we failed the OpenGLVersion check, log it. if (override) { auto accountManager = DependencyManager::get(); From d2a2ba18462f93a02eaffa5f7ac7f97949dc35b0 Mon Sep 17 00:00:00 2001 From: kunalgosar Date: Tue, 7 Mar 2017 17:19:21 -0800 Subject: [PATCH 05/17] Fixed recording bug --- libraries/avatars/src/AvatarData.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 06df75d451..3c33451f02 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2000,6 +2000,11 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { } } + auto currentBasis = getRecordingBasis(); + if (!currentBasis) { + currentBasis = std::make_shared(Transform::fromJson(json[JSON_AVATAR_BASIS])); + } + if (json.contains(JSON_AVATAR_RELATIVE)) { // During playback you can either have the recording basis set to the avatar current state // meaning that all playback is relative to this avatars starting position, or @@ -2008,15 +2013,14 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { // The first is more useful for playing back recordings on your own avatar, while // the latter is more useful for playing back other avatars within your scene. - auto currentBasis = getRecordingBasis(); - if (!currentBasis) { - currentBasis = std::make_shared(Transform::fromJson(json[JSON_AVATAR_BASIS])); - } - auto relativeTransform = Transform::fromJson(json[JSON_AVATAR_RELATIVE]); auto worldTransform = currentBasis->worldTransform(relativeTransform); setPosition(worldTransform.getTranslation()); setOrientation(worldTransform.getRotation()); + } else { + // We still set the position in the case that there is no movement. + setPosition(currentBasis->getTranslation()); + setOrientation(currentBasis->getRotation()); } if (json.contains(JSON_AVATAR_SCALE)) { From adc3581d63cad51d359e556d9f6e82b65991ada2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 7 Mar 2017 19:32:34 -0800 Subject: [PATCH 06/17] remove override of locationChanged which was keeping the render-bounds of Line3DOverlays from updating during calls to editOverlay --- interface/src/ui/overlays/Line3DOverlay.cpp | 5 ----- interface/src/ui/overlays/Line3DOverlay.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index e1ea06c599..900c79fc3f 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -258,8 +258,3 @@ QVariant Line3DOverlay::getProperty(const QString& property) { Line3DOverlay* Line3DOverlay::createClone() const { return new Line3DOverlay(this); } - - -void Line3DOverlay::locationChanged(bool tellPhysics) { - // do nothing -} diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index aceecff6b2..c9ceac55a9 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -48,8 +48,6 @@ public: virtual Line3DOverlay* createClone() const override; - virtual void locationChanged(bool tellPhysics = true) override; - glm::vec3 getDirection() const { return _direction; } float getLength() const { return _length; } glm::vec3 getLocalStart() const { return getLocalPosition(); } From 371c20ee250a7ceab3f13559547a3ea506c6d4d4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 8 Mar 2017 10:33:59 -0800 Subject: [PATCH 07/17] hide tablet by making it not visible rather than destroying it --- scripts/system/libraries/WebTablet.js | 19 ++++- scripts/system/tablet-ui/tabletUI.js | 101 ++++++++++++++++++++++---- 2 files changed, 101 insertions(+), 19 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 8a0831a3cd..1acc12b3f1 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -45,6 +45,10 @@ function calcSpawnInfo(hand, height) { 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 (!hand) { + hand = NO_HANDS; + } + if (HMD.active && hand !== NO_HANDS) { var handController = getControllerWorldLocation(hand, true); var controllerPosition = handController.position; @@ -96,7 +100,7 @@ function calcSpawnInfo(hand, height) { * @param hand [number] -1 indicates no hand, Controller.Standard.RightHand or Controller.Standard.LeftHand * @param clientOnly [bool] true indicates tablet model is only visible to client. */ -WebTablet = function (url, width, dpi, hand, clientOnly) { +WebTablet = function (url, width, dpi, hand, clientOnly, location, invisisble) { var _this = this; @@ -129,11 +133,16 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { "grabbableKey": {"grabbable": true} }), dimensions: {x: this.width, y: this.height, z: this.depth}, - parentID: AVATAR_SELF_ID + parentID: AVATAR_SELF_ID, + visible: !invisisble }; // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(hand, true, tabletProperties); + if (location) { + tabletProperties.localPosition = location.localPosition; + tabletProperties.localRotation = location.localRotation; + } this.cleanUpOldTablets(); @@ -164,7 +173,8 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { parentID: this.tabletEntityID, parentJointIndex: -1, showKeyboardFocusHighlight: false, - isAA: HMD.active + isAA: HMD.active, + visible: !invisisble }); var HOME_BUTTON_Y_OFFSET = (this.height / 2) - 0.009; @@ -177,7 +187,8 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { visible: true, drawInFront: false, parentID: this.tabletEntityID, - parentJointIndex: -1 + parentJointIndex: -1, + visible: !invisisble }); this.receive = function (channel, senderID, senderUUID, localOnly) { diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index d5065cb826..14eb7e7ecc 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -12,21 +12,22 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, MyAvatar */ +/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, + MyAvatar, Menu, Vec3 */ (function() { // BEGIN LOCAL_SCOPE var tabletShown = false; + var tabletRezzed = false; var tabletLocation = null; var activeHand = null; + var DEFAULT_WIDTH = 0.4375; + var DEFAULT_TABLET_SCALE = 100; + UIWebTablet = null; Script.include("../libraries/WebTablet.js"); - function showTabletUI() { - tabletShown = true; - print("show tablet-ui"); - - var DEFAULT_WIDTH = 0.4375; - var DEFAULT_TABLET_SCALE = 100; + function rezTablet(invisible) { + print("XXX show tablet-ui, not rezzed. position is " + JSON.stringify(tabletLocation)); var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; var TABLET_SCALE = DEFAULT_TABLET_SCALE; if (toolbarMode) { @@ -34,37 +35,88 @@ } else { TABLET_SCALE = Settings.getValue("hmdTabletScale") || DEFAULT_TABLET_SCALE; } - UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", DEFAULT_WIDTH * (TABLET_SCALE / 100), null, activeHand, true); + + UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", + DEFAULT_WIDTH * (TABLET_SCALE / 100), + null, activeHand, true, tabletLocation, invisible); UIWebTablet.register(); HMD.tabletID = UIWebTablet.tabletEntityID; HMD.homeButtonID = UIWebTablet.homeButtonID; HMD.tabletScreenID = UIWebTablet.webOverlayID; + tabletRezzed = true; + } + + function showTabletUI() { + tabletShown = true; + + if (!tabletRezzed) { + rezTablet(false); + } + + if (UIWebTablet && tabletRezzed) { + if (tabletLocation) { + print("XXX show tablet-ui, already rezzed, already position: " + JSON.stringify(tabletLocation)); + Overlays.editOverlay(HMD.tabletID, { + localPosition: tabletLocation.localPosition, + localRotation: tabletLocation.localRotation, + visible: true + }); + } else { + print("XXX show tablet-ui, already rezzed, no position"); + var tabletProperties = {}; + UIWebTablet.calculateTabletAttachmentProperties(activeHand, true, tabletProperties); + tabletProperties.visible = true; + Overlays.editOverlay(HMD.tabletID, tabletProperties); + } + Overlays.editOverlay(HMD.homeButtonID, { visible: true }); + Overlays.editOverlay(HMD.tabletScreenID, { visible: true }); + } } function hideTabletUI() { tabletShown = false; - print("hide tablet-ui"); + if (UIWebTablet) { + tabletLocation = UIWebTablet.getLocation(); + print("XXX hide tablet-ui, position was " + JSON.stringify(tabletLocation)); + // Overlays.editOverlay(HMD.tabletID, { localPosition: { x: -1000, y: 0, z:0 } }); + Overlays.editOverlay(HMD.tabletID, { visible: false }); + Overlays.editOverlay(HMD.homeButtonID, { visible: false }); + Overlays.editOverlay(HMD.tabletScreenID, { visible: false }); + } else { + print("XXX hide tablet-ui, UIWebTablet is null"); + } + } + + function closeTabletUI() { + tabletShown = false; if (UIWebTablet) { if (UIWebTablet.onClose) { UIWebTablet.onClose(); } tabletLocation = UIWebTablet.getLocation(); + print("XXX close tablet-ui, position was " + JSON.stringify(tabletLocation)); UIWebTablet.unregister(); UIWebTablet.destroy(); UIWebTablet = null; HMD.tabletID = null; HMD.homeButtonID = null; HMD.tabletScreenID = null; + } else { + print("XXX close tablet-ui, UIWebTablet is null"); } + tabletRezzed = false; } + function updateShowTablet() { // close the WebTablet if it we go into toolbar mode. var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; + var visibleToOthers = Settings.getValue("tabletVisibleToOthers"); + if (tabletShown && toolbarMode) { - hideTabletUI(); + closeTabletUI(); HMD.closeTablet(); return; } @@ -78,18 +130,37 @@ tablet.updateAudioBar(currentMicLevel); } - if (tabletShown && UIWebTablet && Overlays.getOverlayType(UIWebTablet.webOverlayID) != "web3d") { + // XXX don't do this check every time? + if (tabletRezzed && UIWebTablet && Overlays.getOverlayType(UIWebTablet.webOverlayID) != "web3d") { // when we switch domains, the tablet entity gets destroyed and recreated. this causes // the overlay to be deleted, but not recreated. If the overlay is deleted for this or any // other reason, close the tablet. - hideTabletUI(); + closeTabletUI(); HMD.closeTablet(); - } else if (HMD.showTablet && !tabletShown && !toolbarMode) { - UserActivityLogger.openedTablet(Settings.getValue("tabletVisibleToOthers")); + print("XXX autodestroying tablet"); + } + + + if (HMD.showTablet && !tabletShown && !toolbarMode) { + UserActivityLogger.openedTablet(visibleToOthers); showTabletUI(); } else if (!HMD.showTablet && tabletShown) { UserActivityLogger.closedTablet(); - hideTabletUI(); + if (visibleToOthers) { + closeTabletUI(); + } else { + hideTabletUI(); + } + } else if (!toolbarMode && !visibleToOthers && !tabletRezzed) { + // pre-make the tablet so it will appear quickly + tabletLocation = { + localPosition: Vec3.sum(MyAvatar.position, { x: -1000, y: 0, z: 0 }), + localRotation: { x: 0, y: 0, z: 0, w: 1 } + }; + print("XXX pre-creating tablet at " + JSON.stringify(tabletLocation)); + rezTablet(true); + tabletLocation = null; + tabletShown = false; } } From ce105e0329b03ac8086afcea6e79d714417b4546 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 8 Mar 2017 22:21:57 +0000 Subject: [PATCH 08/17] fix double tablets when reloading scripts --- scripts/system/tablet-ui/tabletUI.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index d5065cb826..fd73578328 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -131,7 +131,9 @@ } Script.scriptEnding.connect(function () { - Entities.deleteEntity(HMD.tabletID); + var tabletID = HMD.tabletID; + Entities.deleteEntity(tabletID); + Overlays.deleteOverlay(tabletID) HMD.tabletID = null; HMD.homeButtonID = null; HMD.tabletScreenID = null; From ae48dae96f08722388c830acbf19d5dc77b50568 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 8 Mar 2017 15:29:46 -0800 Subject: [PATCH 09/17] get rid of toggleTabletUI lockout period --- interface/src/Application.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d659ec0a94..60d6adc5d1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1620,14 +1620,7 @@ QString Application::getUserAgent() { return userAgent; } -uint64_t lastTabletUIToggle { 0 }; -const uint64_t toggleTabletUILockout { 500000 }; void Application::toggleTabletUI() const { - uint64_t now = usecTimestampNow(); - if (now - lastTabletUIToggle < toggleTabletUILockout) { - return; - } - lastTabletUIToggle = now; auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); bool messageOpen = tablet->isMessageDialogOpen(); From 28ade45f97f12bf9d9d50dab5a8f85422c3fcd77 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 8 Mar 2017 15:30:15 -0800 Subject: [PATCH 10/17] make maxFPS a property --- interface/src/ui/overlays/Web3DOverlay.cpp | 26 ++++++++++++++++++++-- interface/src/ui/overlays/Web3DOverlay.h | 4 ++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index d8bead0a07..dd161a9994 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -179,12 +179,20 @@ void Web3DOverlay::loadSourceURL() { tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data()); // Override min fps for tablet UI, for silky smooth scrolling - _webSurface->setMaxFps(90); + setMaxFPS(90); } } _webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition())); } +void Web3DOverlay::setMaxFPS(uint8_t maxFPS) { + _desiredMaxFPS = maxFPS; + if (_webSurface) { + _webSurface->setMaxFps(_desiredMaxFPS); + _currentMaxFPS = _desiredMaxFPS; + } +} + void Web3DOverlay::render(RenderArgs* args) { if (!_visible || !getParentVisible()) { return; @@ -194,9 +202,11 @@ void Web3DOverlay::render(RenderArgs* args) { QSurface * currentSurface = currentContext->surface(); if (!_webSurface) { _webSurface = DependencyManager::get()->acquire(pickURL()); - _webSurface->setMaxFps(10); // FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces // and the current rendering load) + if (_currentMaxFPS != _desiredMaxFPS) { + setMaxFPS(_desiredMaxFPS); + } loadSourceURL(); _webSurface->resume(); _webSurface->resize(QSize(_resolution.x, _resolution.y)); @@ -246,6 +256,10 @@ void Web3DOverlay::render(RenderArgs* args) { _emitScriptEventConnection = connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); _webEventReceivedConnection = connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); + } else { + if (_currentMaxFPS != _desiredMaxFPS) { + setMaxFPS(_desiredMaxFPS); + } } vec2 halfSize = getSize() / 2.0f; @@ -401,6 +415,11 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { _dpi = dpi.toFloat(); } + auto maxFPS = properties["maxFPS"]; + if (maxFPS.isValid()) { + _desiredMaxFPS = maxFPS.toInt(); + } + auto showKeyboardFocusHighlight = properties["showKeyboardFocusHighlight"]; if (showKeyboardFocusHighlight.isValid()) { _showKeyboardFocusHighlight = showKeyboardFocusHighlight.toBool(); @@ -420,6 +439,9 @@ QVariant Web3DOverlay::getProperty(const QString& property) { if (property == "dpi") { return _dpi; } + if (property == "maxFPS") { + return _desiredMaxFPS; + } if (property == "showKeyboardFocusHighlight") { return _showKeyboardFocusHighlight; } diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 2b9686919d..e71cac2452 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -31,6 +31,7 @@ public: QString pickURL(); void loadSourceURL(); + void setMaxFPS(uint8_t maxFPS); virtual void render(RenderArgs* args) override; virtual const render::ShapeKey getShapeKey() override; @@ -75,6 +76,9 @@ private: bool _pressed{ false }; QTouchDevice _touchDevice; + uint8_t _desiredMaxFPS { 10 }; + uint8_t _currentMaxFPS { 0 }; + QMetaObject::Connection _mousePressConnection; QMetaObject::Connection _mouseReleaseConnection; QMetaObject::Connection _mouseMoveConnection; From de2dd7383db495782efcc3e30d9fd2f4140640bf Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 8 Mar 2017 15:30:49 -0800 Subject: [PATCH 11/17] didn't need invis flag, after all --- scripts/system/libraries/WebTablet.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 1acc12b3f1..ed0b0e75f7 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -100,7 +100,7 @@ function calcSpawnInfo(hand, height) { * @param hand [number] -1 indicates no hand, Controller.Standard.RightHand or Controller.Standard.LeftHand * @param clientOnly [bool] true indicates tablet model is only visible to client. */ -WebTablet = function (url, width, dpi, hand, clientOnly, location, invisisble) { +WebTablet = function (url, width, dpi, hand, clientOnly, location) { var _this = this; @@ -133,8 +133,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, invisisble) { "grabbableKey": {"grabbable": true} }), dimensions: {x: this.width, y: this.height, z: this.depth}, - parentID: AVATAR_SELF_ID, - visible: !invisisble + parentID: AVATAR_SELF_ID }; // compute position, rotation & parentJointIndex of the tablet @@ -173,8 +172,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, invisisble) { parentID: this.tabletEntityID, parentJointIndex: -1, showKeyboardFocusHighlight: false, - isAA: HMD.active, - visible: !invisisble + isAA: HMD.active }); var HOME_BUTTON_Y_OFFSET = (this.height / 2) - 0.009; @@ -187,8 +185,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, invisisble) { visible: true, drawInFront: false, parentID: this.tabletEntityID, - parentJointIndex: -1, - visible: !invisisble + parentJointIndex: -1 }); this.receive = function (channel, senderID, senderUUID, localOnly) { From 6aac2eb338be3e7c66f6b378a6dd1f1124c33370 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 8 Mar 2017 15:31:24 -0800 Subject: [PATCH 12/17] for overlay tablets, precreate them so that they show up quickly when summoned --- scripts/system/tablet-ui/tabletUI.js | 139 ++++++++++++++++++--------- 1 file changed, 92 insertions(+), 47 deletions(-) diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 14eb7e7ecc..53989201d2 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -13,21 +13,46 @@ // /* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, - MyAvatar, Menu, Vec3 */ + MyAvatar, Menu */ (function() { // BEGIN LOCAL_SCOPE var tabletShown = false; var tabletRezzed = false; - var tabletLocation = null; var activeHand = null; var DEFAULT_WIDTH = 0.4375; var DEFAULT_TABLET_SCALE = 100; + var preMakeTime = Date.now(); + var validCheckTime = Date.now(); + var debugTablet = false; UIWebTablet = null; Script.include("../libraries/WebTablet.js"); - function rezTablet(invisible) { - print("XXX show tablet-ui, not rezzed. position is " + JSON.stringify(tabletLocation)); + function tabletIsValid() { + if (!UIWebTablet) { + return false; + } + if (UIWebTablet.tabletIsOverlay && Overlays.getProperty(HMD.tabletID, "type") != "model") { + if (debugTablet) { + print("TABLET is invalid due to frame: " + JSON.stringify(Overlays.getProperty(HMD.tabletID, "type"))); + } + return false; + } + if (Overlays.getProperty(HMD.homeButtonID, "type") != "sphere" || + Overlays.getProperty(HMD.tabletScreenID, "type") != "web3d") { + if (debugTablet) { + print("TABLET is invalid due to other"); + } + return false; + } + return true; + } + + + function rezTablet() { + if (debugTablet) { + print("TABLET rezzing"); + } var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; var TABLET_SCALE = DEFAULT_TABLET_SCALE; if (toolbarMode) { @@ -38,11 +63,12 @@ UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", DEFAULT_WIDTH * (TABLET_SCALE / 100), - null, activeHand, true, tabletLocation, invisible); + null, activeHand, true); UIWebTablet.register(); HMD.tabletID = UIWebTablet.tabletEntityID; HMD.homeButtonID = UIWebTablet.homeButtonID; HMD.tabletScreenID = UIWebTablet.webOverlayID; + tabletRezzed = true; } @@ -54,36 +80,42 @@ } if (UIWebTablet && tabletRezzed) { - if (tabletLocation) { - print("XXX show tablet-ui, already rezzed, already position: " + JSON.stringify(tabletLocation)); - Overlays.editOverlay(HMD.tabletID, { - localPosition: tabletLocation.localPosition, - localRotation: tabletLocation.localRotation, - visible: true - }); - } else { - print("XXX show tablet-ui, already rezzed, no position"); - var tabletProperties = {}; - UIWebTablet.calculateTabletAttachmentProperties(activeHand, true, tabletProperties); - tabletProperties.visible = true; + if (debugTablet) { + print("TABLET in showTabletUI, already rezzed"); + } + var tabletProperties = {}; + UIWebTablet.calculateTabletAttachmentProperties(activeHand, true, tabletProperties); + tabletProperties.visible = true; + if (UIWebTablet.tabletIsOverlay) { Overlays.editOverlay(HMD.tabletID, tabletProperties); } Overlays.editOverlay(HMD.homeButtonID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { visible: true }); + Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 90 }); } } function hideTabletUI() { tabletShown = false; - if (UIWebTablet) { - tabletLocation = UIWebTablet.getLocation(); - print("XXX hide tablet-ui, position was " + JSON.stringify(tabletLocation)); - // Overlays.editOverlay(HMD.tabletID, { localPosition: { x: -1000, y: 0, z:0 } }); - Overlays.editOverlay(HMD.tabletID, { visible: false }); - Overlays.editOverlay(HMD.homeButtonID, { visible: false }); - Overlays.editOverlay(HMD.tabletScreenID, { visible: false }); + if (!UIWebTablet) { + return; + } + + if (UIWebTablet.tabletIsOverlay) { + if (debugTablet) { + print("TABLET hide"); + } + if (Settings.getValue("tabletVisibleToOthers")) { + closeTabletUI(); + } else { + // Overlays.editOverlay(HMD.tabletID, { localPosition: { x: -1000, y: 0, z:0 } }); + Overlays.editOverlay(HMD.tabletID, { visible: false }); + Overlays.editOverlay(HMD.homeButtonID, { visible: false }); + Overlays.editOverlay(HMD.tabletScreenID, { visible: false }); + Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 1 }); + } } else { - print("XXX hide tablet-ui, UIWebTablet is null"); + closeTabletUI(); } } @@ -94,22 +126,25 @@ UIWebTablet.onClose(); } - tabletLocation = UIWebTablet.getLocation(); - print("XXX close tablet-ui, position was " + JSON.stringify(tabletLocation)); + if (debugTablet) { + print("TABLET close"); + } UIWebTablet.unregister(); UIWebTablet.destroy(); UIWebTablet = null; HMD.tabletID = null; HMD.homeButtonID = null; HMD.tabletScreenID = null; - } else { - print("XXX close tablet-ui, UIWebTablet is null"); + } else if (debugTablet) { + print("TABLET closeTabletUI, UIWebTablet is null"); } tabletRezzed = false; } function updateShowTablet() { + var MSECS_PER_SEC = 1000.0; + var now = Date.now(); // close the WebTablet if it we go into toolbar mode. var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; @@ -130,14 +165,18 @@ tablet.updateAudioBar(currentMicLevel); } - // XXX don't do this check every time? - if (tabletRezzed && UIWebTablet && Overlays.getOverlayType(UIWebTablet.webOverlayID) != "web3d") { - // when we switch domains, the tablet entity gets destroyed and recreated. this causes - // the overlay to be deleted, but not recreated. If the overlay is deleted for this or any - // other reason, close the tablet. - closeTabletUI(); - HMD.closeTablet(); - print("XXX autodestroying tablet"); + if (validCheckTime - now > MSECS_PER_SEC) { + validCheckTime = now; + if (tabletRezzed && UIWebTablet && !tabletIsValid()) { + // when we switch domains, the tablet entity gets destroyed and recreated. this causes + // the overlay to be deleted, but not recreated. If the overlay is deleted for this or any + // other reason, close the tablet. + closeTabletUI(); + HMD.closeTablet(); + if (debugTablet) { + print("TABLET autodestroying"); + } + } } @@ -151,17 +190,23 @@ } else { hideTabletUI(); } - } else if (!toolbarMode && !visibleToOthers && !tabletRezzed) { - // pre-make the tablet so it will appear quickly - tabletLocation = { - localPosition: Vec3.sum(MyAvatar.position, { x: -1000, y: 0, z: 0 }), - localRotation: { x: 0, y: 0, z: 0, w: 1 } - }; - print("XXX pre-creating tablet at " + JSON.stringify(tabletLocation)); - rezTablet(true); - tabletLocation = null; - tabletShown = false; } + + // if the tablet is an overlay, attempt to pre-create it and then hide it so that when it's + // summoned, it will appear quickly. + if (!toolbarMode && !visibleToOthers) { + if (now - preMakeTime > MSECS_PER_SEC) { + preMakeTime = now; + if (!tabletIsValid()) { + closeTabletUI(); + rezTablet(false); + tabletShown = false; + } else if (!tabletShown) { + hideTabletUI(); + } + } + } + } function toggleHand(channel, hand, senderUUID, localOnly) { From 5f7a0ea8fc0857723cc7f579eb54e49d06e355f7 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 9 Mar 2017 00:05:26 +0000 Subject: [PATCH 13/17] Close tablet when switching form HMD to desktop and vice versa --- scripts/system/hmd.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index c206a76e3f..8174cd960c 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -21,10 +21,12 @@ var desktopMenuItemName = "Desktop"; ['OpenVR (Vive)', 'Oculus Rift'].forEach(function (name) { if (!headset && Menu.menuItemExists(displayMenuName, name)) { headset = name; + } }); -var controllerDisplay = false; + + var controllerDisplay = false; function updateControllerDisplay() { if (HMD.active && Menu.isOptionChecked("Third Person")) { if (!controllerDisplay) { @@ -37,7 +39,9 @@ function updateControllerDisplay() { } } -var button; + + var button; + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through. @@ -45,6 +49,7 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode']; function onHmdChanged(isHmd) { + HMD.closeTablet(); if (isHmd) { button.editProperties({ icon: "icons/tablet-icons/switch-desk-i.svg", From 561bd27c38e4997e4deba36fd1dd483120c36894 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 9 Mar 2017 00:08:50 +0000 Subject: [PATCH 14/17] minimize diff --- scripts/system/hmd.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 8174cd960c..452801c786 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -21,7 +21,6 @@ var desktopMenuItemName = "Desktop"; ['OpenVR (Vive)', 'Oculus Rift'].forEach(function (name) { if (!headset && Menu.menuItemExists(displayMenuName, name)) { headset = name; - } }); @@ -40,7 +39,7 @@ function updateControllerDisplay() { } - var button; +var button; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); From f6d799c048818e4bd7d043c6154a0ae96c370b51 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 9 Mar 2017 00:09:32 +0000 Subject: [PATCH 15/17] minimize diff --- scripts/system/hmd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 452801c786..af4abf0788 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -25,7 +25,7 @@ var desktopMenuItemName = "Desktop"; }); - var controllerDisplay = false; +var controllerDisplay = false; function updateControllerDisplay() { if (HMD.active && Menu.isOptionChecked("Third Person")) { if (!controllerDisplay) { From b878f164fbf0a18191bb73a56d4e523141d2f2a0 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 9 Mar 2017 00:10:38 +0000 Subject: [PATCH 16/17] minimize diff again --- scripts/system/hmd.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index af4abf0788..39f8338d72 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -24,7 +24,6 @@ var desktopMenuItemName = "Desktop"; } }); - var controllerDisplay = false; function updateControllerDisplay() { if (HMD.active && Menu.isOptionChecked("Third Person")) { @@ -38,9 +37,7 @@ function updateControllerDisplay() { } } - -var button; - +var button; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through. From 2a275a4f4b7dbb448a3b23a16f7cd3f30a12e7e0 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 9 Mar 2017 00:15:02 +0000 Subject: [PATCH 17/17] removed tab at the end of word --- scripts/system/hmd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 39f8338d72..c545e6bcee 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -37,7 +37,7 @@ function updateControllerDisplay() { } } -var button; +var button; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through.