From e7d14b938177785e0a23b023d422dd7f8ea71cd2 Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Thu, 5 Jan 2017 16:49:55 -0800 Subject: [PATCH 01/22] Adding new property "showKeyboardFocusHighlight" to Web3DOverlay --- interface/src/ui/overlays/Web3DOverlay.cpp | 8 ++++++++ interface/src/ui/overlays/Web3DOverlay.h | 1 + 2 files changed, 9 insertions(+) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index fe48b05f5e..960536fe63 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -355,6 +355,11 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { if (dpi.isValid()) { _dpi = dpi.toFloat(); } + + auto showKeyboardFocusHighlight = properties["showKeyboardFocusHighlight"]; + if (showKeyboardFocusHighlight.isValid()) { + _showKeyboardFocusHighlight = showKeyboardFocusHighlight.toBool; + } } QVariant Web3DOverlay::getProperty(const QString& property) { @@ -370,6 +375,9 @@ QVariant Web3DOverlay::getProperty(const QString& property) { if (property == "dpi") { return _dpi; } + if (property == "showKeyboardFocusHighlight") { + return _showKeyboardFocusHighlight; + } return Billboard3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index db45b073e2..18fefb6885 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -68,6 +68,7 @@ private: float _dpi; vec2 _resolution{ 640, 480 }; int _geometryId { 0 }; + bool _showKeyboardFocusHighlight{ true }; bool _pressed{ false }; QTouchDevice _touchDevice; From d0d89e216d9f116e55447383f0484eb495784e8e Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Mon, 9 Jan 2017 09:54:41 -0800 Subject: [PATCH 02/22] Add showKeyboardFocusHighlight in constructor --- interface/src/ui/overlays/Web3DOverlay.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 960536fe63..eb93fe6f47 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -50,7 +50,8 @@ Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : _url(Web3DOverlay->_url), _scriptURL(Web3DOverlay->_scriptURL), _dpi(Web3DOverlay->_dpi), - _resolution(Web3DOverlay->_resolution) + _resolution(Web3DOverlay->_resolution), + _showKeyboardFocusHighlight(Web3DOverlay->_showKeyboardFocusHighlight) { _geometryId = DependencyManager::get()->allocateID(); } From fa803aebe9327c3e454edf4c821cd9af2abe9358 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 10:21:07 -0800 Subject: [PATCH 03/22] use /~/ style to reference includes --- scripts/system/controllers/handControllerGrab.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2475314873..942418780e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -19,9 +19,9 @@ (function() { // BEGIN LOCAL_SCOPE -Script.include("../libraries/utils.js"); -Script.include("../libraries/Xform.js"); -Script.include("../libraries/controllers.js"); +Script.include("/~/system/libraries/utils.js"); +Script.include("/~/system/libraries/Xform.js"); +Script.include("/~/system/libraries/controllers.js"); // // add lines where the hand ray picking is happening From 2fa17ea6a7857df5da6a3485db76c08c7ce00caa Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Mon, 9 Jan 2017 10:23:31 -0800 Subject: [PATCH 04/22] fix missing parenthesis --- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index eb93fe6f47..bd21305023 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -359,7 +359,7 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { auto showKeyboardFocusHighlight = properties["showKeyboardFocusHighlight"]; if (showKeyboardFocusHighlight.isValid()) { - _showKeyboardFocusHighlight = showKeyboardFocusHighlight.toBool; + _showKeyboardFocusHighlight = showKeyboardFocusHighlight.toBool(); } } From 93e4f9126f8133a4e81c1348fab3422831615f7d Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Mon, 9 Jan 2017 11:01:15 -0800 Subject: [PATCH 05/22] disable drawing highlight if "showKeyboardFocusHighlight" flag is false --- interface/src/Application.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 212c06d410..e4966dc172 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4076,9 +4076,11 @@ void Application::setKeyboardFocusOverlay(unsigned int overlayID) { } _lastAcceptedKeyPress = usecTimestampNow(); - auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR; - const float OVERLAY_DEPTH = 0.0105f; - setKeyboardFocusHighlight(overlay->getPosition(), overlay->getRotation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH)); + if (overlay->getProperty("showKeyboardFocusHighlight").toBool()) { + auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR; + const float OVERLAY_DEPTH = 0.0105f; + setKeyboardFocusHighlight(overlay->getPosition(), overlay->getRotation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH)); + } } } } From 5323e7f34dcec8e22242a9e39a4468d62aa718c8 Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Mon, 9 Jan 2017 13:36:58 -0800 Subject: [PATCH 06/22] set showKeyboardHighlight to false when initialising tablet --- scripts/system/libraries/WebTablet.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index c894642f80..d4e9f3194a 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -90,7 +90,8 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { color: { red: 255, green: 255, blue: 255 }, alpha: 1.0, parentID: this.tabletEntityID, - parentJointIndex: -1 + parentJointIndex: -1, + showKeyboardFocusHighlight: false }); var HOME_BUTTON_Y_OFFSET = -0.25; From c39eb6294e164cba89669f8764400dac5be80999 Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Mon, 9 Jan 2017 14:01:59 -0800 Subject: [PATCH 07/22] undo --- scripts/system/libraries/WebTablet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index d4e9f3194a..2bd6c6a6a1 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -91,7 +91,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { alpha: 1.0, parentID: this.tabletEntityID, parentJointIndex: -1, - showKeyboardFocusHighlight: false + showKeyboardFocusHighlight: true }); var HOME_BUTTON_Y_OFFSET = -0.25; From 32eda3a49cc7a3903a79e49ee808fae2055f157d Mon Sep 17 00:00:00 2001 From: Faye Li Si Fi Date: Mon, 9 Jan 2017 14:02:14 -0800 Subject: [PATCH 08/22] redo --- scripts/system/libraries/WebTablet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 2bd6c6a6a1..d4e9f3194a 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -91,7 +91,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { alpha: 1.0, parentID: this.tabletEntityID, parentJointIndex: -1, - showKeyboardFocusHighlight: true + showKeyboardFocusHighlight: false }); var HOME_BUTTON_Y_OFFSET = -0.25; From 61f0c56a3672b4c6fe64b8308e8a97947b3d27d9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 14:20:03 -0800 Subject: [PATCH 09/22] remove code that remembers previous state in userData --- .../system/controllers/handControllerGrab.js | 376 ++---------------- 1 file changed, 28 insertions(+), 348 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 942418780e..9a7262dbe6 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -12,7 +12,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global setEntityCustomData, getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, +/* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, setGrabCommunications, Menu */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ @@ -27,7 +27,7 @@ Script.include("/~/system/libraries/controllers.js"); // add lines where the hand ray picking is happening // var WANT_DEBUG = false; -var WANT_DEBUG_STATE = false; +var WANT_DEBUG_STATE = true; var WANT_DEBUG_SEARCH_NAME = null; var FORCE_IGNORE_IK = false; @@ -41,8 +41,6 @@ var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smo var TRIGGER_OFF_VALUE = 0.1; var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab -var COLLIDE_WITH_AV_AFTER_RELEASE_DELAY = 0.25; // seconds - var BUMPER_ON_VALUE = 0.5; var THUMB_ON_VALUE = 0.5; @@ -175,7 +173,6 @@ var GRABBABLE_PROPERTIES = [ ]; var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js -var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js var DEFAULT_GRABBABLE_DATA = { disableReleaseVelocity: false @@ -188,6 +185,15 @@ var blacklist = []; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; +var holdEnabled = true; +var nearGrabEnabled = true; +var farGrabEnabled = true; +var myAvatarScalingEnabled = true; +var objectScalingEnabled = true; +var mostRecentSearchingHand = RIGHT_HAND; +var DEFAULT_SPHERE_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/equip-Fresnel-3.fbx"; +var HARDWARE_MOUSE_ID = 0; // Value reserved for hardware mouse. + // states for the state machine var STATE_OFF = 0; var STATE_SEARCHING = 1; @@ -201,31 +207,8 @@ var STATE_ENTITY_LASER_TOUCHING = 8; var STATE_OVERLAY_STYLUS_TOUCHING = 9; var STATE_OVERLAY_LASER_TOUCHING = 10; -var holdEnabled = true; -var nearGrabEnabled = true; -var farGrabEnabled = true; -var myAvatarScalingEnabled = true; -var objectScalingEnabled = true; - -// "collidesWith" is specified by comma-separated list of group names -// the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar -var COLLIDES_WITH_WHILE_GRABBED = "dynamic,otherAvatar"; - -var HEART_BEAT_INTERVAL = 5 * MSECS_PER_SEC; -var HEART_BEAT_TIMEOUT = 15 * MSECS_PER_SEC; - -var delayedDeactivateFunc; -var delayedDeactivateTimeout; -var delayedDeactivateEntityID; - var CONTROLLER_STATE_MACHINE = {}; -var mostRecentSearchingHand = RIGHT_HAND; - -var DEFAULT_SPHERE_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/equip-Fresnel-3.fbx"; - -var HARDWARE_MOUSE_ID = 0; // Value reserved for hardware mouse. - CONTROLLER_STATE_MACHINE[STATE_OFF] = { name: "off", enterMethod: "offEnter", @@ -491,17 +474,6 @@ function storeAttachPointForHotspotInSettings(hotspot, hand, offsetPosition, off setAttachPointSettings(attachPointSettings); } -function removeMyAvatarFromCollidesWith(origCollidesWith) { - var collidesWithSplit = origCollidesWith.split(","); - // remove myAvatar from the array - for (var i = collidesWithSplit.length - 1; i >= 0; i--) { - if (collidesWithSplit[i] === "myAvatar") { - collidesWithSplit.splice(i, 1); - } - } - return collidesWithSplit.join(); -} - // If another script is managing the reticle (as is done by HandControllerPointer), we should not be setting it here, // and we should not be showing lasers when someone else is using the Reticle to indicate a 2D minor mode. var EXTERNALLY_MANAGED_2D_MINOR_MODE = true; @@ -1271,11 +1243,6 @@ function MyController(hand) { this.prevPotentialEquipHotspot = potentialEquipHotspot; }; - this.heartBeatIsStale = function(data) { - var now = Date.now(); - return data.heartBeat === undefined || now - data.heartBeat > HEART_BEAT_TIMEOUT; - }; - // Performs ray pick test from the hand controller into the world // @param {number} which hand to use, RIGHT_HAND or LEFT_HAND // @returns {object} returns object with two keys entityID and distance @@ -1391,14 +1358,13 @@ function MyController(hand) { this.hotspotIsEquippable = function(hotspot) { var props = entityPropertiesCache.getProps(hotspot.entityID); - var grabProps = entityPropertiesCache.getGrabProps(hotspot.entityID); var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); - var refCount = ("refCount" in grabProps) ? grabProps.refCount : 0; var okToEquipFromOtherHand = ((this.getOtherHandController().state == STATE_NEAR_GRABBING || - this.getOtherHandController().state == STATE_DISTANCE_HOLDING) && - this.getOtherHandController().grabbedEntity == hotspot.entityID); - if (refCount > 0 && !this.heartBeatIsStale(grabProps) && !okToEquipFromOtherHand) { + this.getOtherHandController().state == STATE_DISTANCE_HOLDING) && + this.getOtherHandController().grabbedEntity == hotspot.entityID); + var hasParent = !(props.parentID === NULL_UUID); + if ((hasParent || entityHasActions(hotspot.entityID)) && !okToEquipFromOtherHand) { if (debug) { print("equip is skipping '" + props.name + "': grabbed by someone else"); } @@ -1410,29 +1376,13 @@ function MyController(hand) { this.entityIsGrabbable = function(entityID) { var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); - var grabProps = entityPropertiesCache.getGrabProps(entityID); var props = entityPropertiesCache.getProps(entityID); if (!props) { return false; } - var physical = propsArePhysical(props); - var grabbable = false; var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); - var refCount = ("refCount" in grabProps) ? grabProps.refCount : 0; - - if (physical) { - // physical things default to grabbable - grabbable = true; - } else { - // non-physical things default to non-grabbable unless they are already grabbed - if (refCount > 0) { - grabbable = true; - } else { - grabbable = false; - } - } - - if (grabbableProps.hasOwnProperty("grabbable") && refCount === 0) { + var grabbable = propsArePhysical(props); + if (grabbableProps.hasOwnProperty("grabbable")) { grabbable = grabbableProps.grabbable; } @@ -1640,17 +1590,6 @@ function MyController(hand) { } else { // If near something grabbable, grab it! if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && nearGrabEnabled) { - var props = entityPropertiesCache.getProps(entity); - var grabProps = entityPropertiesCache.getGrabProps(entity); - var refCount = grabProps.refCount ? grabProps.refCount : 0; - if (refCount >= 1) { - // if another person is holding the object, remember to restore the - // parent info, when we are finished grabbing it. - this.shouldResetParentOnRelease = true; - this.previousParentID = props.parentID; - this.previousParentJointIndex = props.parentJointIndex; - } - this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); return; } else { @@ -2039,7 +1978,6 @@ function MyController(hand) { this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC); if (this.actionID !== null) { - this.activateEntity(this.grabbedEntity, grabbedProperties, false, true); this.callEntityMethodOnGrabbed("startDistanceGrab"); } @@ -2063,12 +2001,11 @@ function MyController(hand) { Entities.editEntity(this.grabbedEntity, { velocity: velocity }); } + this.release(); this.setState(STATE_OFF, "trigger released"); return; } - this.heartBeat(this.grabbedEntity); - var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; @@ -2101,8 +2038,6 @@ function MyController(hand) { disableMoveWithHead: false }; - var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); - // Update radialVelocity var lastVelocity = Vec3.multiply(worldHandDelta, 1.0 / deltaObjectTime); var delta = Vec3.normalize(Vec3.subtract(grabbedProperties.position, worldControllerPosition)); @@ -2133,6 +2068,7 @@ function MyController(hand) { newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition); var objectToAvatar = Vec3.subtract(this.currentObjectPosition, MyAvatar.position); + var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); if (handControllerData.disableMoveWithHead !== true) { // mix in head motion if (MOVE_WITH_HEAD) { @@ -2280,12 +2216,10 @@ function MyController(hand) { } var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - this.activateEntity(this.grabbedEntity, grabbedProperties, false, false); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); if (FORCE_IGNORE_IK) { this.ignoreIK = true; } else { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; } @@ -2372,17 +2306,9 @@ function MyController(hand) { } Entities.editEntity(this.grabbedEntity, { - velocity: { - x: 0, - y: 0, - z: 0 - }, - angularVelocity: { - x: 0, - y: 0, - z: 0 - }, - dynamic: false + velocity: { x: 0, y: 0, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0 }, + // dynamic: false }); if (this.state == STATE_NEAR_GRABBING) { @@ -2408,6 +2334,7 @@ function MyController(hand) { if (this.state == STATE_NEAR_GRABBING && (!this.triggerClicked && this.secondaryReleased())) { this.callEntityMethodOnGrabbed("releaseGrab"); + this.release(); this.setState(STATE_OFF, "trigger released"); return; } @@ -2425,8 +2352,8 @@ function MyController(hand) { // we have an equipped object and the secondary trigger was released // short-circuit the other checks and release it this.preparingHoldRelease = false; - this.release(); + this.setState(STATE_OFF, "equipping ended via secondary press"); return; } @@ -2466,14 +2393,13 @@ function MyController(hand) { this.prevDropDetected = dropDetected; } - this.heartBeat(this.grabbedEntity); - var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position", "rotation", "dimensions", "registrationPoint"]); if (!props.position) { // server may have reset, taking our equipped entity with it. move back to "off" state this.callEntityMethodOnGrabbed("releaseGrab"); + this.release(); this.setState(STATE_OFF, "entity has no position property"); return; } @@ -2508,6 +2434,7 @@ function MyController(hand) { } else { // this.state == STATE_HOLD this.callEntityMethodOnGrabbed("releaseEquip"); } + this.release(); this.setState(STATE_OFF, "held object too far away"); return; } @@ -2902,7 +2829,6 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - var noVelocity = false; if (this.grabbedEntity !== null) { if (this.state === STATE_HOLD) { this.callEntityMethodOnGrabbed("releaseEquip"); @@ -2910,26 +2836,11 @@ function MyController(hand) { // Make a small release haptic pulse if we really were holding something Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); - - // If this looks like the release after adjusting something still held in the other hand, print the position - // and rotation of the held thing to help content creators set the userData. - var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {}); - this.printNewOffsets = (grabData.refCount > 1); - if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); - // sometimes we want things to stay right where they are when we let go. - var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - if (releaseVelocityData.disableReleaseVelocity === true || - // this next line allowed both: - // (1) far-grab, pull to self, near grab, then throw - // (2) equip something physical and adjust it with a other-hand grab without the thing drifting - grabData.refCount > 1) { - noVelocity = true; - } } - this.deactivateEntity(this.grabbedEntity, noVelocity); + Entities.editEntity(this.grabbedEntity, { parentID: NULL_UUID }); // XXX Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'release', @@ -2953,96 +2864,6 @@ function MyController(hand) { this.grabPointSphereOff(); }; - this.heartBeat = function(entityID) { - var now = Date.now(); - if (now - this.lastHeartBeat > HEART_BEAT_INTERVAL) { - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - data.heartBeat = now; - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - this.lastHeartBeat = now; - } - }; - - this.resetAbandonedGrab = function(entityID) { - print("cleaning up abandoned grab on " + entityID); - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - data.refCount = 1; - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - this.deactivateEntity(entityID, false); - }; - - this.activateEntity = function(entityID, grabbedProperties, wasLoaded, collideWithStatic) { - this.autoUnequipCounter = 0; - - if (this.entityActivated) { - return; - } - this.entityActivated = true; - - if (delayedDeactivateTimeout && delayedDeactivateEntityID == entityID) { - // we have a timeout waiting to set collisions with myAvatar back on (so that when something - // is thrown it doesn't collide with the avatar's capsule the moment it's released). We've - // regrabbed the entity before the timeout fired, so cancel the timeout, run the function now - // and adjust the grabbedProperties. This will make the saved set of properties (the ones that - // get re-instated after all the grabs have been released) be correct. - Script.clearTimeout(delayedDeactivateTimeout); - delayedDeactivateTimeout = null; - grabbedProperties.collidesWith = delayedDeactivateFunc(); - } - - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - var now = Date.now(); - - if (wasLoaded) { - data.refCount = 1; - } else { - data.refCount = data.refCount ? data.refCount + 1 : 1; - - // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done - if (data.refCount == 1) { - data.heartBeat = now; - this.lastHeartBeat = now; - - this.isInitialGrab = true; - data.gravity = grabbedProperties.gravity; - data.collidesWith = grabbedProperties.collidesWith; - data.collisionless = grabbedProperties.collisionless; - data.dynamic = grabbedProperties.dynamic; - data.parentID = wasLoaded ? NULL_UUID : grabbedProperties.parentID; - data.parentJointIndex = grabbedProperties.parentJointIndex; - - var whileHeldProperties = { - gravity: { x: 0, y: 0, z: 0 }, - "collidesWith": collideWithStatic ? - COLLIDES_WITH_WHILE_GRABBED + ",static" : - COLLIDES_WITH_WHILE_GRABBED - }; - Entities.editEntity(entityID, whileHeldProperties); - } else if (data.refCount > 1) { - if (this.heartBeatIsStale(data)) { - // this entity has userData suggesting it is grabbed, but nobody is updating the hearbeat. - // deactivate it before grabbing. - this.resetAbandonedGrab(entityID); - grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - return this.activateEntity(entityID, grabbedProperties, wasLoaded, false); - } - - this.isInitialGrab = false; - // if an object is being grabbed by more than one person (or the same person twice, but nevermind), switch - // the collision groups so that it wont collide with "other" avatars. This avoids a situation where two - // people are holding something and one of them will be able (if the other releases at the right time) to - // bootstrap themselves with the held object. This happens because the meaning of "otherAvatar" in - // the collision mask hinges on who the physics simulation owner is. - Entities.editEntity(entityID, { - // "collidesWith": removeAvatarsFromCollidesWith(grabbedProperties.collidesWith) - collisionless: true - }); - } - } - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - return data; - }; - this.checkForStrayChildren = function() { // sometimes things can get parented to a hand and this script is unaware. Search for such entities and // unhook them. @@ -3060,147 +2881,6 @@ function MyController(hand) { }); }; - this.delayedDeactivateEntity = function(entityID, collidesWith) { - // If, before the grab started, the held entity collided with myAvatar, we do the deactivation in - // two parts. Most of it is done in deactivateEntity(), but the final collidesWith and refcount - // are delayed a bit. This keeps thrown things from colliding with the avatar's capsule so often. - // The refcount is handled in this delayed fashion so things don't get confused if someone else - // grabs the entity before the timeout fires. - Entities.editEntity(entityID, { - collidesWith: collidesWith - }); - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - if (data && data.refCount) { - data.refCount = data.refCount - 1; - if (data.refCount < 1) { - data = null; - } - } else { - data = null; - } - - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - }; - - this.deactivateEntity = function(entityID, noVelocity, delayed) { - var deactiveProps; - - if (!this.entityActivated) { - return; - } - this.entityActivated = false; - - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - var doDelayedDeactivate = false; - if (data && data.refCount) { - data.refCount = data.refCount - 1; - if (data.refCount < 1) { - deactiveProps = { - gravity: data.gravity, - // don't set collidesWith myAvatar back right away, because thrown things tend to bounce off the - // avatar's capsule. - collidesWith: removeMyAvatarFromCollidesWith(data.collidesWith), - collisionless: data.collisionless, - dynamic: data.dynamic, - parentID: data.parentID, - parentJointIndex: data.parentJointIndex - }; - - doDelayedDeactivate = (data.collidesWith.indexOf("myAvatar") >= 0); - - if (doDelayedDeactivate) { - var delayedCollidesWith = data.collidesWith; - var delayedEntityID = entityID; - delayedDeactivateFunc = function() { - // set collidesWith back to original value a bit later than the rest - delayedDeactivateTimeout = null; - _this.delayedDeactivateEntity(delayedEntityID, delayedCollidesWith); - return delayedCollidesWith; - }; - delayedDeactivateTimeout = - Script.setTimeout(delayedDeactivateFunc, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY * MSECS_PER_SEC); - delayedDeactivateEntityID = entityID; - } - - // things that are held by parenting and dropped with no velocity will end up as "static" in bullet. If - // it looks like the dropped thing should fall, give it a little velocity. - var props = Entities.getEntityProperties(entityID, ["parentID", "velocity", "dynamic", "shapeType"]); - var parentID = props.parentID; - - if (!noVelocity && - parentID == MyAvatar.sessionUUID && - Vec3.length(data.gravity) > 0.0 && - data.dynamic && - data.parentID == NULL_UUID && - !data.collisionless) { - deactiveProps.velocity = this.currentVelocity; - } - if (noVelocity) { - deactiveProps.velocity = { - x: 0.0, - y: 0.0, - z: 0.0 - }; - deactiveProps.angularVelocity = { - x: 0.0, - y: 0.0, - z: 0.0 - }; - } - - Entities.editEntity(entityID, deactiveProps); - data = null; - } else if (this.shouldResetParentOnRelease) { - // we parent-grabbed this from another parent grab. try to put it back where we found it. - deactiveProps = { - parentID: this.previousParentID, - parentJointIndex: this.previousParentJointIndex, - velocity: { - x: 0.0, - y: 0.0, - z: 0.0 - }, - angularVelocity: { - x: 0.0, - y: 0.0, - z: 0.0 - } - }; - Entities.editEntity(entityID, deactiveProps); - - if (this.printNewOffsets) { - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]); - if (grabbedProperties && grabbedProperties.localPosition && grabbedProperties.localRotation) { - print((this.hand === RIGHT_HAND ? '"LeftHand"' : '"RightHand"') + ":" + - '[{"x":' + grabbedProperties.localPosition.x + ', "y":' + grabbedProperties.localPosition.y + - ', "z":' + grabbedProperties.localPosition.z + '}, {"x":' + grabbedProperties.localRotation.x + - ', "y":' + grabbedProperties.localRotation.y + ', "z":' + grabbedProperties.localRotation.z + - ', "w":' + grabbedProperties.localRotation.w + '}]'); - } - } - } else if (noVelocity) { - Entities.editEntity(entityID, { - velocity: { - x: 0.0, - y: 0.0, - z: 0.0 - }, - angularVelocity: { - x: 0.0, - y: 0.0, - z: 0.0 - }, - dynamic: data.dynamic - }); - } - } else { - data = null; - } - if (!doDelayedDeactivate) { - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - } - }; - this.getOtherHandController = function() { return (this.hand === RIGHT_HAND) ? leftController : rightController; }; From 01ff3f14b6fd3d7ece2fe6d440a8e6a4d9ac9f15 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 15:28:05 -0800 Subject: [PATCH 10/22] re-evaluate an entities in-bullet status when parentID changes --- libraries/entities/src/EntityItem.cpp | 9 ++++++++- libraries/entities/src/EntityItem.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 00c9ab2dde..d6792d3871 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1308,7 +1308,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref); SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription); SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, setParentID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, updateParentID); SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex); SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube); @@ -1565,6 +1565,13 @@ void EntityItem::updatePosition(const glm::vec3& value) { } } +void EntityItem::updateParentID(const QUuid& value) { + if (_parentID != value) { + setParentID(value); + _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; // children are forced to be kinematic + } +} + void EntityItem::updatePositionFromNetwork(const glm::vec3& value) { if (shouldSuppressLocationEdits()) { return; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index bd5fb44fc8..7be81391f3 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -339,6 +339,7 @@ public: // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags virtual void updateRegistrationPoint(const glm::vec3& value); void updatePosition(const glm::vec3& value); + void updateParentID(const QUuid& value); void updatePositionFromNetwork(const glm::vec3& value); void updateDimensions(const glm::vec3& value); void updateRotation(const glm::quat& rotation); From 45a26d599fd74dfa7abba925c60c96ffd03a1738 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 16:40:28 -0800 Subject: [PATCH 11/22] if something is a child of the local avatar, never let it collide with MY_AVATAR --- libraries/entities/src/EntityItem.cpp | 7 +++++-- libraries/shared/src/SpatiallyNestable.cpp | 2 +- libraries/shared/src/SpatiallyNestable.h | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index d6792d3871..8cf5265fae 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1780,7 +1780,6 @@ void EntityItem::updateCreated(uint64_t value) { } void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const { - // TODO: detect attachment status and adopt group of wearer if (_collisionless) { group = BULLET_COLLISION_GROUP_COLLISIONLESS; mask = 0; @@ -1794,10 +1793,14 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } uint8_t userMask = getCollisionMask(); + if (hasAncestorOfType(NestableType::Avatar)) { + userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; + } + if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) != (bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) { // asymmetric avatar collision mask bits - if (!getSimulatorID().isNull() && (!getSimulatorID().isNull()) && getSimulatorID() != Physics::getSessionUUID()) { + if (!getSimulatorID().isNull() && getSimulatorID() != Physics::getSessionUUID()) { // someone else owns the simulation, so we toggle the avatar bits (swap interpretation) userMask ^= USER_COLLISION_MASK_AVATARS | ~userMask; } diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index cbe982b959..4876277d67 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1015,7 +1015,7 @@ AACube SpatiallyNestable::getQueryAACube() const { return result; } -bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) { +bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { bool success; SpatiallyNestablePointer parent = getParentPointer(success); if (!success || !parent) { diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index f58e2c906c..25a6254689 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -163,7 +163,7 @@ public: bool isParentIDValid() const { bool success = false; getParentPointer(success); return success; } virtual SpatialParentTree* getParentTree() const { return nullptr; } - bool hasAncestorOfType(NestableType nestableType); + bool hasAncestorOfType(NestableType nestableType) const; SpatiallyNestablePointer getParentPointer(bool& success) const; static SpatiallyNestablePointer findByID(QUuid id, bool& success); From ad45bbf5ba00a412f79383d0f2b89839da295495 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 16:45:54 -0800 Subject: [PATCH 12/22] set DIRTY_COLLISION_GROUP flag when parentID changes --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8cf5265fae..e869225dd0 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1569,6 +1569,7 @@ void EntityItem::updateParentID(const QUuid& value) { if (_parentID != value) { setParentID(value); _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; // children are forced to be kinematic + _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar } } From 35577f49d092416b2101549ba981df196ba73dca Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 17:00:39 -0800 Subject: [PATCH 13/22] change the test for when MY_AVATAR collisions are short-circuited --- libraries/entities/src/EntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index e869225dd0..b073f30b03 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1794,7 +1794,8 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } uint8_t userMask = getCollisionMask(); - if (hasAncestorOfType(NestableType::Avatar)) { + QUuid parentID = getParentID(); + if (!parentID.isNull() && parentID == Physics::getSessionUUID()) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } From 4a29cce58464a7ae5af23abcab32c153fe9f02ce Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Jan 2017 06:17:40 -0800 Subject: [PATCH 14/22] adjust when USER_COLLISION_GROUP_MY_AVATAR is auto-disabled --- libraries/entities/src/EntityItem.cpp | 7 +++++-- libraries/shared/src/SpatiallyNestable.cpp | 14 ++++++++++++++ libraries/shared/src/SpatiallyNestable.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b073f30b03..27b3507f31 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1794,8 +1794,11 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } uint8_t userMask = getCollisionMask(); - QUuid parentID = getParentID(); - if (!parentID.isNull() && parentID == Physics::getSessionUUID()) { + // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the + // "bootstrapping" problem where you can shoot yourself across the room by grabbing something + // and holding it against your own avatar. + QUuid ancestorID = findAncestorOfType(NestableType::Avatar); + if (!ancestorID.isNull() && ancestorID == Physics::getSessionUUID()) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 4876277d67..de998799b3 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1029,6 +1029,20 @@ bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { return parent->hasAncestorOfType(nestableType); } +const QUuid SpatiallyNestable::findAncestorOfType(NestableType nestableType) const { + bool success; + SpatiallyNestablePointer parent = getParentPointer(success); + if (!success || !parent) { + return QUuid(); + } + + if (parent->_nestableType == nestableType) { + return parent->getID(); + } + + return parent->findAncestorOfType(nestableType); +} + void SpatiallyNestable::getLocalTransformAndVelocities( Transform& transform, glm::vec3& velocity, diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 25a6254689..bf60b2966d 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -164,6 +164,7 @@ public: virtual SpatialParentTree* getParentTree() const { return nullptr; } bool hasAncestorOfType(NestableType nestableType) const; + const QUuid findAncestorOfType(NestableType nestableType) const; SpatiallyNestablePointer getParentPointer(bool& success) const; static SpatiallyNestablePointer findByID(QUuid id, bool& success); From c99f897db1eb1ecd098ee03e95369b2417f88206 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 4 Jan 2017 23:47:18 +0000 Subject: [PATCH 15/22] first commit --- .../src/TabletScriptingInterface.cpp | 17 +++++++++++++++++ .../src/TabletScriptingInterface.h | 3 +++ scripts/system/libraries/WebTablet.js | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 056d946482..3b5c292f26 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -14,6 +14,8 @@ #include #include #include "ScriptEngineLogging.h" +#include "DependencyManager.h" +#include "OffscreenUi.h" TabletScriptingInterface::TabletScriptingInterface() { qmlRegisterType("Hifi", 1, 0, "SoundEffect"); @@ -56,6 +58,7 @@ void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, QQuickItem* qm static const char* TABLET_SOURCE_URL = "Tablet.qml"; static const char* WEB_VIEW_SOURCE_URL = "TabletWebView.qml"; static const char* LOADER_SOURCE_PROPERTY_NAME = "LoaderSource"; +static const char* VRMENU_SOURCE_URL = "VrMenuView.qml"; TabletProxy::TabletProxy(QString name) : _name(name) { ; @@ -95,6 +98,16 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr } } +void TabletProxy::gotoMenuScreen() { + if (_qmlTabletRoot) { + _qmlTabletRoot->setProperty(LOADER_SOURCE_PROPERTY_NAME, TABLET_SOURCE_URL); + auto loader = _qmlTabletRoot->findChild("loader"); + auto offscreenUi = DependencyManager::get(); + auto rootMenu = offscreenUi->getRootMenu(); + QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL))); + } +} + void TabletProxy::gotoHomeScreen() { if (_qmlTabletRoot) { QString tabletSource = _qmlTabletRoot->property(LOADER_SOURCE_PROPERTY_NAME).toString(); @@ -186,6 +199,10 @@ void TabletProxy::addButtonsToHomeScreen() { QObject::disconnect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen())); } +void TabletProxy::addButtonsToMenuScreen() { + +} + void TabletProxy::removeButtonsFromHomeScreen() { auto tabletScriptingInterface = DependencyManager::get(); for (auto& buttonProxy : _tabletButtonProxies) { diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index 43750e2519..f49c9ea498 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -65,6 +65,8 @@ public: void setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface); + Q_INVOKABLE void gotoMenuScreen(); + /**jsdoc * transition to the home screen * @function TabletProxy#gotoHomeScreen @@ -120,6 +122,7 @@ signals: private slots: void addButtonsToHomeScreen(); + void addButtonsToMenuScreen(); protected: void removeButtonsFromHomeScreen(); QQuickItem* getQmlTablet() const; diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index beb54ae3fa..12f655554e 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -118,7 +118,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { _this.clicked = true; } var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.gotoHomeScreen(); + tablet.gotoMenuScreen(); } }; From 3fab901b495f30644cfd951e7e8f8bc9a7bf5af8 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 5 Jan 2017 17:58:42 +0000 Subject: [PATCH 16/22] commit chabges --- .../resources/qml/hifi/tablet/TabletMenu.qml | 12 +++++++++ .../src/TabletScriptingInterface.cpp | 25 +++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 interface/resources/qml/hifi/tablet/TabletMenu.qml diff --git a/interface/resources/qml/hifi/tablet/TabletMenu.qml b/interface/resources/qml/hifi/tablet/TabletMenu.qml new file mode 100644 index 0000000000..00227a6312 --- /dev/null +++ b/interface/resources/qml/hifi/tablet/TabletMenu.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 +import QtGraphicalEffects 1.0 +"../../menus" +Item { + id: tabletMenu + objectName: "menu" + property var rootMenu: Menu { objectName: "rootMenu" } + + Rectangle { + color: "#2b2b2b" + } +} diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 3b5c292f26..f740a508a9 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -58,7 +58,7 @@ void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, QQuickItem* qm static const char* TABLET_SOURCE_URL = "Tablet.qml"; static const char* WEB_VIEW_SOURCE_URL = "TabletWebView.qml"; static const char* LOADER_SOURCE_PROPERTY_NAME = "LoaderSource"; -static const char* VRMENU_SOURCE_URL = "VrMenuView.qml"; +static const char* VRMENU_SOURCE_URL = "TabletMenu.qml"; TabletProxy::TabletProxy(QString name) : _name(name) { ; @@ -101,9 +101,8 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr void TabletProxy::gotoMenuScreen() { if (_qmlTabletRoot) { _qmlTabletRoot->setProperty(LOADER_SOURCE_PROPERTY_NAME, TABLET_SOURCE_URL); - auto loader = _qmlTabletRoot->findChild("loader"); - auto offscreenUi = DependencyManager::get(); - auto rootMenu = offscreenUi->getRootMenu(); + //auto loader = _qmlTabletRoot->findChild("loader"); + //QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen())); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL))); } } @@ -200,7 +199,23 @@ void TabletProxy::addButtonsToHomeScreen() { } void TabletProxy::addButtonsToMenuScreen() { - + if (!_qmlTabletRoot) { + return; + } + + auto loader = _qmlTabletRoot->findChild("loader"); + if (!loader) { + return; + } + + QQuickItem* VrMenu = loader->findChild("VrMenu"); + if (!VrMenu) { + qDebug() << "----------> could not find vr menu"; + return; + } + + QString name = "Menu"; + QVariant returnedValue; } void TabletProxy::removeButtonsFromHomeScreen() { From 6c51edec7764381a1017194c97aed57814416057 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 6 Jan 2017 22:29:07 +0000 Subject: [PATCH 17/22] added context menu to tablet --- .../resources/qml/hifi/tablet/TabletMenu.qml | 27 ++- .../qml/hifi/tablet/TabletMenuItem.qml | 113 ++++++++++++ .../qml/hifi/tablet/TabletMenuView.qml | 117 +++++++++++++ .../qml/hifi/tablet/TabletMouseHandler.qml | 163 ++++++++++++++++++ .../src/TabletScriptingInterface.cpp | 15 +- libraries/ui/src/OffscreenUi.cpp | 4 + libraries/ui/src/OffscreenUi.h | 4 +- scripts/defaultScripts.js | 3 +- scripts/system/menu.js | 30 ++++ 9 files changed, 460 insertions(+), 16 deletions(-) create mode 100644 interface/resources/qml/hifi/tablet/TabletMenuItem.qml create mode 100644 interface/resources/qml/hifi/tablet/TabletMenuView.qml create mode 100644 interface/resources/qml/hifi/tablet/TabletMouseHandler.qml create mode 100644 scripts/system/menu.js diff --git a/interface/resources/qml/hifi/tablet/TabletMenu.qml b/interface/resources/qml/hifi/tablet/TabletMenu.qml index 00227a6312..36de421a78 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenu.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenu.qml @@ -1,12 +1,27 @@ -import QtQuick 2.0 +import QtQuick 2.5 import QtGraphicalEffects 1.0 -"../../menus" +import QtQuick.Controls 1.4 +import QtQml 2.2 +import "." + + Item { id: tabletMenu - objectName: "menu" - property var rootMenu: Menu { objectName: "rootMenu" } + objectName: "tabletMenu" - Rectangle { - color: "#2b2b2b" + width: parent.width + height: parent.height + + property var rootMenu: Menu { objectName:"rootMenu" } + property var point: Qt.point(50, 50) + + TabletMouseHandler { id: menuPopperUpper } + + function setRootMenu(menu) { + tabletMenu.rootMenu = menu + buildMenu() + } + function buildMenu() { + menuPopperUpper.popup(tabletMenu, rootMenu.items) } } diff --git a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml new file mode 100644 index 0000000000..ce45ca7065 --- /dev/null +++ b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml @@ -0,0 +1,113 @@ +// +// VrMenuItem.qml +// +// Created by Bradley Austin Davis on 29 Apr 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import "../../controls-uit" +import "../../styles-uit" + +Item { + id: root + HifiConstants { id: hifi } + property alias text: label.text + property var source + + implicitHeight: source.visible ? 2 * label.implicitHeight : 0 + implicitWidth: 2 * hifi.dimensions.menuPadding.x + check.width + label.width + tail.width + visible: source.visible + width: parent.width + + CheckBox { + id: check + // FIXME: Should use radio buttons if source.exclusiveGroup. + anchors { + left: parent.left + leftMargin: hifi.dimensions.menuPadding.x + top: label.top + topMargin: 0 + } + width: 20 + visible: source.visible && source.type === 1 && source.checkable + checked: setChecked() + function setChecked() { + if (!source || source.type !== 1 || !source.checkable) { + return false; + } + // FIXME this works for native QML menus but I don't think it will + // for proxied QML menus + return source.checked; + } + } + + RalewaySemiBold { + id: label + size: hifi.fontSizes.rootMenu + font.capitalization: isSubMenu ? Font.MixedCase : Font.AllUppercase + anchors.left: check.right + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50 + enabled: source.visible && (source.type !== 0 ? source.enabled : false) + visible: source.visible + } + + Item { + id: separator + anchors { + fill: parent + leftMargin: hifi.dimensions.menuPadding.x + check.width + rightMargin: hifi.dimensions.menuPadding.x + tail.width + } + visible: source.type === MenuItemType.Separator + + Rectangle { + anchors { + left: parent.left + right: parent.right + verticalCenter: parent.verticalCenter + } + height: 1 + color: hifi.colors.lightGray50 + } + } + + Item { + id: tail + width: 48 + (shortcut.visible ? shortcut.width : 0) + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + rightMargin: hifi.dimensions.menuPadding.x + } + + RalewayLight { + id: shortcut + text: source.shortcut ? source.shortcut : "" + size: hifi.fontSizes.shortcutText + color: hifi.colors.baseGrayShadow + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: 15 + visible: source.visible && text != "" + } + + HiFiGlyphs { + text: hifi.glyphs.disclosureExpand + color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow25 + size: 2 * hifi.fontSizes.rootMenuDisclosure + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + horizontalAlignment: Text.AlignRight + visible: source.visible && (source.type === 2) + } + } +} diff --git a/interface/resources/qml/hifi/tablet/TabletMenuView.qml b/interface/resources/qml/hifi/tablet/TabletMenuView.qml new file mode 100644 index 0000000000..b3a6bcf811 --- /dev/null +++ b/interface/resources/qml/hifi/tablet/TabletMenuView.qml @@ -0,0 +1,117 @@ +// +// VrMenuView.qml +// +// Created by Bradley Austin Davis on 18 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import "../../styles-uit" + +FocusScope { + id: root + implicitHeight: background.height + implicitWidth: background.width + + property alias currentItem: listView.currentItem + property alias model: listView.model + property bool isSubMenu: false + signal selected(var item) + + HifiConstants { id: hifi } + + Rectangle { + id: background + anchors.fill: listView + radius: hifi.dimensions.borderRadius + border.width: hifi.dimensions.borderWidth + border.color: hifi.colors.lightGrayText80 + color: isSubMenu ? hifi.colors.faintGray : hifi.colors.faintGray80 + } + + ListView { + id: listView + x: 8; y: 8 + width: parent.width + height: parent.height + topMargin: hifi.dimensions.menuPadding.y + onEnabledChanged: recalcSize(); + onVisibleChanged: recalcSize(); + onCountChanged: recalcSize(); + focus: true + highlightMoveDuration: 0 + + highlight: Rectangle { + anchors { + left: parent ? parent.left : undefined + right: parent ? parent.right : undefined + leftMargin: hifi.dimensions.borderWidth + rightMargin: hifi.dimensions.borderWidth + } + color: hifi.colors.white + } + + delegate: TabletMenuItem { + text: name + source: item + onImplicitHeightChanged: listView.recalcSize() + onImplicitWidthChanged: listView.recalcSize() + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: listView.currentIndex = index + onClicked: root.selected(item) + } + } + + function recalcSize() { + if (model.count !== count || !visible) { + return; + } + + var originalIndex = currentIndex; + var maxWidth = width; + var newHeight = 0; + for (var i = 0; i < count; ++i) { + currentIndex = i; + if (!currentItem) { + continue; + } + if (currentItem && currentItem.implicitWidth > maxWidth) { + maxWidth = currentItem.implicitWidth + } + if (currentItem.visible) { + newHeight += currentItem.implicitHeight + } + } + newHeight += 2 * hifi.dimensions.menuPadding.y; // White space at top and bottom. + if (maxWidth > width) { + width = maxWidth; + } + if (newHeight > height) { + height = newHeight + } + currentIndex = originalIndex; + } + + function previousItem() { currentIndex = (currentIndex + count - 1) % count; } + function nextItem() { currentIndex = (currentIndex + count + 1) % count; } + function selectCurrentItem() { if (currentIndex != -1) root.selected(currentItem.source); } + + Keys.onUpPressed: previousItem(); + Keys.onDownPressed: nextItem(); + Keys.onSpacePressed: selectCurrentItem(); + Keys.onRightPressed: selectCurrentItem(); + Keys.onReturnPressed: selectCurrentItem(); + } +} + + + diff --git a/interface/resources/qml/hifi/tablet/TabletMouseHandler.qml b/interface/resources/qml/hifi/tablet/TabletMouseHandler.qml new file mode 100644 index 0000000000..db74de82fb --- /dev/null +++ b/interface/resources/qml/hifi/tablet/TabletMouseHandler.qml @@ -0,0 +1,163 @@ +// +// MessageDialog.qml +// +// Created by Bradley Austin Davis on 18 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +import "." + +Item { + id: root + anchors.fill: parent + objectName: "tabletMenuHandlerItem" + + MouseArea { + id: menuRoot; + objectName: "tabletMenuHandlerMouseArea" + anchors.fill: parent + enabled: d.topMenu !== null + onClicked: { + d.clearMenus(); + } + } + + QtObject { + id: d + property var menuStack: [] + property var topMenu: null; + property var modelMaker: Component { ListModel { } } + property var menuViewMaker: Component { + TabletMenuView { + id: subMenu + onSelected: d.handleSelection(subMenu, currentItem, item) + } + } + property var delay: Timer { // No setTimeout in QML. + property var menuItem: null; + interval: 0 + repeat: false + running: false + function trigger(item) { // Capture item and schedule asynchronous Timer. + menuItem = item; + start(); + } + onTriggered: { + menuItem.trigger(); // Now trigger the item. + } + } + + function toModel(items) { + var result = modelMaker.createObject(tabletMenu); + for (var i = 0; i < items.length; ++i) { + var item = items[i]; + if (!item.visible) continue; + console.log(item.title) + switch (item.type) { + case MenuItemType.Menu: + result.append({"name": item.title, "item": item}) + break; + case MenuItemType.Item: + result.append({"name": item.text, "item": item}) + break; + case MenuItemType.Separator: + result.append({"name": "", "item": item}) + break; + } + } + return result; + } + + function popMenu() { + if (menuStack.length) { + menuStack.pop().destroy(); + } + if (menuStack.length) { + topMenu = menuStack[menuStack.length - 1]; + topMenu.focus = true; + } else { + topMenu = null; + //offscreenFlags.navigationFocused = false; + menuRoot.enabled = false; + } + } + + function pushMenu(newMenu) { + menuStack.push(newMenu); + topMenu = newMenu; + topMenu.focus = true; + //offscreenFlags.navigationFocused = true; + } + + function clearMenus() { + while (menuStack.length) { + popMenu() + } + } + + function clampMenuPosition(menu) { + var margins = 0; + if (menu.x < margins) { + menu.x = margins + } else if ((menu.x + menu.width + margins) > root.width) { + menu.x = root.width - (menu.width + margins); + } + + if (menu.y < 0) { + menu.y = margins + } else if ((menu.y + menu.height + margins) > root.height) { + menu.y = root.height - (menu.height + margins); + } + } + + function buildMenu(items, targetPosition) { + var model = toModel(items); + // Menus must be childed to desktop for Z-ordering + var newMenu = menuViewMaker.createObject(tabletMenu, { model: model, isSubMenu: topMenu !== null }); + pushMenu(newMenu); + return newMenu; + } + + function handleSelection(parentMenu, selectedItem, item) { + while (topMenu && topMenu !== parentMenu) { + popMenu(); + } + + switch (item.type) { + case MenuItemType.Menu: + var target = Qt.vector2d(topMenu.x, topMenu.y).plus(Qt.vector2d(selectedItem.x + 96, selectedItem.y)); + buildMenu(item.items, target).objectName = item.title; + break; + + case MenuItemType.Item: + console.log("Triggering " + item.text) + // Don't block waiting for modal dialogs and such that the menu might open. + delay.trigger(item); + clearMenus(); + break; + } + } + + } + + function popup(parent, items) { + d.clearMenus(); + menuRoot.enabled = true; + d.buildMenu(items, point); + } + + function closeLastMenu() { + if (d.menuStack.length) { + d.popMenu(); + return true; + } + return false; + } + +} diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index f740a508a9..45635d0cbb 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -100,9 +100,9 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr void TabletProxy::gotoMenuScreen() { if (_qmlTabletRoot) { - _qmlTabletRoot->setProperty(LOADER_SOURCE_PROPERTY_NAME, TABLET_SOURCE_URL); - //auto loader = _qmlTabletRoot->findChild("loader"); - //QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen())); + _qmlTabletRoot->setProperty(LOADER_SOURCE_PROPERTY_NAME, VRMENU_SOURCE_URL); + auto loader = _qmlTabletRoot->findChild("loader"); + QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen())); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL))); } } @@ -172,7 +172,7 @@ void TabletProxy::removeButton(QObject* tabletButtonProxy) { void TabletProxy::updateAudioBar(const double micLevel) { auto tablet = getQmlTablet(); if (!tablet) { - qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; + //qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; } else { QMetaObject::invokeMethod(tablet, "setMicLevel", Qt::AutoConnection, Q_ARG(QVariant, QVariant(micLevel))); } @@ -208,14 +208,15 @@ void TabletProxy::addButtonsToMenuScreen() { return; } - QQuickItem* VrMenu = loader->findChild("VrMenu"); + QQuickItem* VrMenu = loader->findChild("tabletMenu"); if (!VrMenu) { qDebug() << "----------> could not find vr menu"; return; } - QString name = "Menu"; - QVariant returnedValue; + auto offscreenUi = DependencyManager::get(); + QObject* menu = offscreenUi->getRootMenu(); + QMetaObject::invokeMethod(VrMenu, "setRootMenu", Qt::AutoConnection, Q_ARG(QVariant, QVariant::fromValue(menu))); } void TabletProxy::removeButtonsFromHomeScreen() { diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 60d80c6b35..3e14d97d40 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -524,6 +524,10 @@ QQuickItem* OffscreenUi::getDesktop() { return _desktop; } +QObject* OffscreenUi::getRootMenu() { + return getRootItem()->findChild("rootMenu"); +} + QQuickItem* OffscreenUi::getToolWindow() { return _toolWindow; } diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 3ab4fa0758..409b70ea3a 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -50,7 +50,7 @@ public: // Setting pinned to true will hide all overlay elements on the desktop that don't have a pinned flag void setPinned(bool pinned = true); - + void togglePinned(); void setConstrainToolbarToCenterX(bool constrained); @@ -59,7 +59,7 @@ public: QObject* getFlags(); QQuickItem* getDesktop(); QQuickItem* getToolWindow(); - + QObject* getRootMenu(); enum Icon { ICON_NONE = 0, ICON_QUESTION, diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 61461c7f06..bd3131f4ff 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -35,7 +35,8 @@ var DEFAULT_SCRIPTS = [ "system/snapshot.js", "system/help.js", "system/bubble.js", - "system/tablet-ui/tabletUI.js" + "system/tablet-ui/tabletUI.js", + "system/menu.js" ]; // add a menu item for debugging diff --git a/scripts/system/menu.js b/scripts/system/menu.js new file mode 100644 index 0000000000..94097271bf --- /dev/null +++ b/scripts/system/menu.js @@ -0,0 +1,30 @@ +// +// menu.js +// scripts/system/ +// +// Created by Dante Ruiz on 5 Jun 2017 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + icon: "icons/tablet-icons/menu-i.svg", + text: "Menu" + }); + + + function onClicked() { + tablet.gotoMenuScreen(); + } + + button.clicked.connect(onClicked); + + Script.scriptEnding.connect(function () { + button.clicked.disconnect(onClicked); + tablet.removeButton(button); + }) +}()); \ No newline at end of file From 670584651852a68e93e03d151684795937229c7f Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 6 Jan 2017 23:25:25 +0000 Subject: [PATCH 18/22] clean up round 1 --- interface/resources/qml/desktop/Desktop.qml | 27 +-------------------- libraries/ui/src/OffscreenUi.cpp | 2 -- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 66b59f0aea..c536020122 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -465,32 +465,7 @@ FocusScope { Component { id: fileDialogBuilder; FileDialog { } } function fileDialog(properties) { return fileDialogBuilder.createObject(desktop, properties); - } - - MenuMouseHandler { id: menuPopperUpper } - function popupMenu(point) { - menuPopperUpper.popup(desktop, rootMenu.items, point); - } - - function toggleMenu(point) { - menuPopperUpper.toggle(desktop, rootMenu.items, point); - } - - Keys.onEscapePressed: { - if (menuPopperUpper.closeLastMenu()) { - event.accepted = true; - return; - } - event.accepted = false; - } - - Keys.onLeftPressed: { - if (menuPopperUpper.closeLastMenu()) { - event.accepted = true; - return; - } - event.accepted = false; - } + } function unfocusWindows() { // First find the active focus item, and unfocus it, all the way diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 3e14d97d40..1814d30c0a 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -538,8 +538,6 @@ void OffscreenUi::unfocusWindows() { } void OffscreenUi::toggleMenu(const QPoint& screenPosition) { // caller should already have mapped using getReticlePosition - emit showDesktop(); // we really only want to do this if you're showing the menu, but for now this works - QMetaObject::invokeMethod(_desktop, "toggleMenu", Q_ARG(QVariant, screenPosition)); } From 2fc76d64fd6becb75bcecb1bf35f8ae71c770524 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 10 Jan 2017 16:31:55 +0000 Subject: [PATCH 19/22] clean up unused code and files --- interface/resources/qml/desktop/Desktop.qml | 1 - .../resources/qml/menus/MenuMouseHandler.qml | 175 ------------------ interface/resources/qml/menus/VrMenuItem.qml | 113 ----------- interface/resources/qml/menus/VrMenuView.qml | 117 ------------ interface/src/Application.cpp | 19 -- interface/src/Application.h | 1 - libraries/ui/src/OffscreenUi.cpp | 3 - libraries/ui/src/OffscreenUi.h | 1 - scripts/system/libraries/WebTablet.js | 2 +- 9 files changed, 1 insertion(+), 431 deletions(-) delete mode 100644 interface/resources/qml/menus/MenuMouseHandler.qml delete mode 100644 interface/resources/qml/menus/VrMenuItem.qml delete mode 100644 interface/resources/qml/menus/VrMenuView.qml diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index c536020122..cc64d0f2b4 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -12,7 +12,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import "../dialogs" -import "../menus" import "../js/Utils.js" as Utils // This is our primary 'desktop' object to which all VR dialogs and windows are childed. diff --git a/interface/resources/qml/menus/MenuMouseHandler.qml b/interface/resources/qml/menus/MenuMouseHandler.qml deleted file mode 100644 index 48574d41e5..0000000000 --- a/interface/resources/qml/menus/MenuMouseHandler.qml +++ /dev/null @@ -1,175 +0,0 @@ -// -// MessageDialog.qml -// -// Created by Bradley Austin Davis on 18 Jan 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -import "." - -Item { - id: root - anchors.fill: parent - objectName: "MouseMenuHandlerItem" - - MouseArea { - id: menuRoot; - objectName: "MouseMenuHandlerMouseArea" - anchors.fill: parent - enabled: d.topMenu !== null - onClicked: { - d.clearMenus(); - } - } - - QtObject { - id: d - property var menuStack: [] - property var topMenu: null; - property var modelMaker: Component { ListModel { } } - property var menuViewMaker: Component { - VrMenuView { - id: subMenu - onSelected: d.handleSelection(subMenu, currentItem, item) - } - } - property var delay: Timer { // No setTimeout in QML. - property var menuItem: null; - interval: 0 - repeat: false - running: false - function trigger(item) { // Capture item and schedule asynchronous Timer. - menuItem = item; - start(); - } - onTriggered: { - menuItem.trigger(); // Now trigger the item. - } - } - - function toModel(items) { - var result = modelMaker.createObject(desktop); - for (var i = 0; i < items.length; ++i) { - var item = items[i]; - if (!item.visible) continue; - switch (item.type) { - case MenuItemType.Menu: - result.append({"name": item.title, "item": item}) - break; - case MenuItemType.Item: - result.append({"name": item.text, "item": item}) - break; - case MenuItemType.Separator: - result.append({"name": "", "item": item}) - break; - } - } - return result; - } - - function popMenu() { - if (menuStack.length) { - menuStack.pop().destroy(); - } - if (menuStack.length) { - topMenu = menuStack[menuStack.length - 1]; - topMenu.focus = true; - } else { - topMenu = null; - offscreenFlags.navigationFocused = false; - menuRoot.enabled = false; - } - } - - function pushMenu(newMenu) { - menuStack.push(newMenu); - topMenu = newMenu; - topMenu.focus = true; - offscreenFlags.navigationFocused = true; - } - - function clearMenus() { - while (menuStack.length) { - popMenu() - } - } - - function clampMenuPosition(menu) { - var margins = 0; - if (menu.x < margins) { - menu.x = margins - } else if ((menu.x + menu.width + margins) > root.width) { - menu.x = root.width - (menu.width + margins); - } - - if (menu.y < 0) { - menu.y = margins - } else if ((menu.y + menu.height + margins) > root.height) { - menu.y = root.height - (menu.height + margins); - } - } - - function buildMenu(items, targetPosition) { - var model = toModel(items); - // Menus must be childed to desktop for Z-ordering - var newMenu = menuViewMaker.createObject(desktop, { model: model, z: topMenu ? topMenu.z + 1 : desktop.zLevels.menu, isSubMenu: topMenu !== null }); - if (targetPosition) { - newMenu.x = targetPosition.x - newMenu.y = targetPosition.y - newMenu.height / 3 * 1 - } - clampMenuPosition(newMenu); - pushMenu(newMenu); - return newMenu; - } - - function handleSelection(parentMenu, selectedItem, item) { - while (topMenu && topMenu !== parentMenu) { - popMenu(); - } - - switch (item.type) { - case MenuItemType.Menu: - var target = Qt.vector2d(topMenu.x, topMenu.y).plus(Qt.vector2d(selectedItem.x + 96, selectedItem.y)); - buildMenu(item.items, target).objectName = item.title; - break; - - case MenuItemType.Item: - console.log("Triggering " + item.text) - // Don't block waiting for modal dialogs and such that the menu might open. - delay.trigger(item); - clearMenus(); - break; - } - } - - } - - function popup(parent, items, point) { - d.clearMenus(); - menuRoot.enabled = true; - d.buildMenu(items, point); - } - - function toggle(parent, items, point) { - if (d.topMenu) { - d.clearMenus(); - return; - } - popup(parent, items, point); - } - - function closeLastMenu() { - if (d.menuStack.length) { - d.popMenu(); - return true; - } - return false; - } - -} diff --git a/interface/resources/qml/menus/VrMenuItem.qml b/interface/resources/qml/menus/VrMenuItem.qml deleted file mode 100644 index 38d2b57c03..0000000000 --- a/interface/resources/qml/menus/VrMenuItem.qml +++ /dev/null @@ -1,113 +0,0 @@ -// -// VrMenuItem.qml -// -// Created by Bradley Austin Davis on 29 Apr 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 -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import "../controls-uit" -import "../styles-uit" - -Item { - id: root - HifiConstants { id: hifi } - property alias text: label.text - property var source - - implicitHeight: source.visible ? 2 * label.implicitHeight : 0 - implicitWidth: 2 * hifi.dimensions.menuPadding.x + check.width + label.width + tail.width - visible: source.visible - width: parent.width - - CheckBox { - id: check - // FIXME: Should use radio buttons if source.exclusiveGroup. - anchors { - left: parent.left - leftMargin: hifi.dimensions.menuPadding.x - top: label.top - topMargin: 0 - } - width: 20 - visible: source.visible && source.type === 1 && source.checkable - checked: setChecked() - function setChecked() { - if (!source || source.type !== 1 || !source.checkable) { - return false; - } - // FIXME this works for native QML menus but I don't think it will - // for proxied QML menus - return source.checked; - } - } - - RalewaySemiBold { - id: label - size: hifi.fontSizes.rootMenu - font.capitalization: isSubMenu ? Font.MixedCase : Font.AllUppercase - anchors.left: check.right - anchors.verticalCenter: parent.verticalCenter - verticalAlignment: Text.AlignVCenter - color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50 - enabled: source.visible && (source.type !== 0 ? source.enabled : false) - visible: source.visible - } - - Item { - id: separator - anchors { - fill: parent - leftMargin: hifi.dimensions.menuPadding.x + check.width - rightMargin: hifi.dimensions.menuPadding.x + tail.width - } - visible: source.type === MenuItemType.Separator - - Rectangle { - anchors { - left: parent.left - right: parent.right - verticalCenter: parent.verticalCenter - } - height: 1 - color: hifi.colors.lightGray50 - } - } - - Item { - id: tail - width: 48 + (shortcut.visible ? shortcut.width : 0) - anchors { - verticalCenter: parent.verticalCenter - right: parent.right - rightMargin: hifi.dimensions.menuPadding.x - } - - RalewayLight { - id: shortcut - text: source.shortcut ? source.shortcut : "" - size: hifi.fontSizes.shortcutText - color: hifi.colors.baseGrayShadow - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: 15 - visible: source.visible && text != "" - } - - HiFiGlyphs { - text: hifi.glyphs.disclosureExpand - color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow25 - size: 2 * hifi.fontSizes.rootMenuDisclosure - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - horizontalAlignment: Text.AlignRight - visible: source.visible && (source.type === 2) - } - } -} diff --git a/interface/resources/qml/menus/VrMenuView.qml b/interface/resources/qml/menus/VrMenuView.qml deleted file mode 100644 index 5db13fc160..0000000000 --- a/interface/resources/qml/menus/VrMenuView.qml +++ /dev/null @@ -1,117 +0,0 @@ -// -// VrMenuView.qml -// -// Created by Bradley Austin Davis on 18 Jan 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import "../styles-uit" - -FocusScope { - id: root - implicitHeight: background.height - implicitWidth: background.width - - property alias currentItem: listView.currentItem - property alias model: listView.model - property bool isSubMenu: false - signal selected(var item) - - HifiConstants { id: hifi } - - Rectangle { - id: background - anchors.fill: listView - radius: hifi.dimensions.borderRadius - border.width: hifi.dimensions.borderWidth - border.color: hifi.colors.lightGrayText80 - color: isSubMenu ? hifi.colors.faintGray : hifi.colors.faintGray80 - } - - ListView { - id: listView - x: 8; y: 8 - width: 128 - height: count * 32 - topMargin: hifi.dimensions.menuPadding.y - onEnabledChanged: recalcSize(); - onVisibleChanged: recalcSize(); - onCountChanged: recalcSize(); - focus: true - highlightMoveDuration: 0 - - highlight: Rectangle { - anchors { - left: parent ? parent.left : undefined - right: parent ? parent.right : undefined - leftMargin: hifi.dimensions.borderWidth - rightMargin: hifi.dimensions.borderWidth - } - color: hifi.colors.white - } - - delegate: VrMenuItem { - text: name - source: item - onImplicitHeightChanged: listView.recalcSize() - onImplicitWidthChanged: listView.recalcSize() - - MouseArea { - anchors.fill: parent - hoverEnabled: true - onEntered: listView.currentIndex = index - onClicked: root.selected(item) - } - } - - function recalcSize() { - if (model.count !== count || !visible) { - return; - } - - var originalIndex = currentIndex; - var maxWidth = width; - var newHeight = 0; - for (var i = 0; i < count; ++i) { - currentIndex = i; - if (!currentItem) { - continue; - } - if (currentItem && currentItem.implicitWidth > maxWidth) { - maxWidth = currentItem.implicitWidth - } - if (currentItem.visible) { - newHeight += currentItem.implicitHeight - } - } - newHeight += 2 * hifi.dimensions.menuPadding.y; // White space at top and bottom. - if (maxWidth > width) { - width = maxWidth; - } - if (newHeight > height) { - height = newHeight - } - currentIndex = originalIndex; - } - - function previousItem() { currentIndex = (currentIndex + count - 1) % count; } - function nextItem() { currentIndex = (currentIndex + count + 1) % count; } - function selectCurrentItem() { if (currentIndex != -1) root.selected(currentItem.source); } - - Keys.onUpPressed: previousItem(); - Keys.onDownPressed: nextItem(); - Keys.onSpacePressed: selectCurrentItem(); - Keys.onRightPressed: selectCurrentItem(); - Keys.onReturnPressed: selectCurrentItem(); - } -} - - - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1458ec0018..f28ed10bdc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1059,10 +1059,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->toggleMute(); } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) { cycleCamera(); - } else if (action == controller::toInt(controller::Action::UI_NAV_SELECT)) { - if (!offscreenUi->navigationFocused()) { - toggleMenuUnderReticle(); - } } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) { toggleTabletUI(); } else if (action == controller::toInt(controller::Action::RETICLE_X)) { @@ -1580,17 +1576,6 @@ QString Application::getUserAgent() { return userAgent; } -void Application::toggleMenuUnderReticle() const { - // In HMD, if the menu is near the mouse but not under it, the reticle can be at a significantly - // different depth. When you focus on the menu, the cursor can appear to your crossed eyes as both - // on the menu and off. - // Even in 2D, it is arguable whether the user would want the menu to be to the side. - const float X_LEFT_SHIFT = 50.0; - auto offscreenUi = DependencyManager::get(); - auto reticlePosition = getApplicationCompositor().getReticlePosition(); - offscreenUi->toggleMenu(QPoint(reticlePosition.x - X_LEFT_SHIFT, reticlePosition.y)); -} - uint64_t lastTabletUIToggle { 0 }; const uint64_t toggleTabletUILockout { 500000 }; void Application::toggleTabletUI() const { @@ -2931,10 +2916,6 @@ void Application::keyPressEvent(QKeyEvent* event) { void Application::keyReleaseEvent(QKeyEvent* event) { - if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) { - toggleMenuUnderReticle(); - } - _keysPressed.remove(event->key()); _controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts diff --git a/interface/src/Application.h b/interface/src/Application.h index c789c705a3..3b89aa52f3 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -484,7 +484,6 @@ private: static void dragEnterEvent(QDragEnterEvent* event); void maybeToggleMenuVisible(QMouseEvent* event) const; - void toggleMenuUnderReticle() const; void toggleTabletUI() const; MainWindow* _window; diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 1814d30c0a..918fb7b54a 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -537,9 +537,6 @@ void OffscreenUi::unfocusWindows() { Q_ASSERT(invokeResult); } -void OffscreenUi::toggleMenu(const QPoint& screenPosition) { // caller should already have mapped using getReticlePosition -} - class FileDialogListener : public ModalDialogListener { Q_OBJECT diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 409b70ea3a..c648e2ec86 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -45,7 +45,6 @@ public: bool navigationFocused(); void setNavigationFocused(bool focused); void unfocusWindows(); - void toggleMenu(const QPoint& screenCoordinates); // Setting pinned to true will hide all overlay elements on the desktop that don't have a pinned flag diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 12f655554e..beb54ae3fa 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -118,7 +118,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { _this.clicked = true; } var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.gotoMenuScreen(); + tablet.gotoHomeScreen(); } }; From bff9e9be3813d389d6499afacc7aec6bce76dd43 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Jan 2017 09:22:36 -0800 Subject: [PATCH 20/22] put back code that replaces previous parentID on release --- scripts/system/controllers/handControllerGrab.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 9a7262dbe6..ebafb167e0 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1363,7 +1363,10 @@ function MyController(hand) { var okToEquipFromOtherHand = ((this.getOtherHandController().state == STATE_NEAR_GRABBING || this.getOtherHandController().state == STATE_DISTANCE_HOLDING) && this.getOtherHandController().grabbedEntity == hotspot.entityID); - var hasParent = !(props.parentID === NULL_UUID); + var hasParent = true; + if (props.parentID === NULL_UUID) { + hasParent = false; + } if ((hasParent || entityHasActions(hotspot.entityID)) && !okToEquipFromOtherHand) { if (debug) { print("equip is skipping '" + props.name + "': grabbed by someone else"); @@ -1591,6 +1594,10 @@ function MyController(hand) { // If near something grabbable, grab it! if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && nearGrabEnabled) { this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); + var props = entityPropertiesCache.getProps(entity); + this.shouldResetParentOnRelease = true; + this.previousParentID = props.parentID; + this.previousParentJointIndex = props.parentJointIndex; return; } else { // potentialNearGrabEntity = entity; @@ -2840,7 +2847,12 @@ function MyController(hand) { Entities.deleteAction(this.grabbedEntity, this.actionID); } - Entities.editEntity(this.grabbedEntity, { parentID: NULL_UUID }); // XXX + if (this.shouldResetParentOnRelease) { + Entities.editEntity(this.grabbedEntity, { + parentID: this.previousParentID, + parentJointIndex: this.previousParentJointIndex + }); + } Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'release', From c98a8168eea50c143f734882422c101aec05b841 Mon Sep 17 00:00:00 2001 From: Faye Li Date: Tue, 10 Jan 2017 09:53:55 -0800 Subject: [PATCH 21/22] uncheck "show meter level" by default --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 4819220400..870d60fdf3 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -160,7 +160,7 @@ Menu::Menu() { audioIO.data(), SLOT(toggleMute())); // Audio > Show Level Meter - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioTools, 0, true); + addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioTools, 0, false); // Avatar menu ---------------------------------- From 07039897c692033133f7a76b209e80fd950e5204 Mon Sep 17 00:00:00 2001 From: Faye Li Date: Tue, 10 Jan 2017 11:41:16 -0800 Subject: [PATCH 22/22] New color scheme for audio bar --- interface/resources/qml/hifi/tablet/Tablet.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml index fbce67a7be..ee3f0a4f32 100644 --- a/interface/resources/qml/hifi/tablet/Tablet.qml +++ b/interface/resources/qml/hifi/tablet/Tablet.qml @@ -127,11 +127,19 @@ Item { gradient: Gradient { GradientStop { position: 0 - color: "#00b8ff" + color: "#2c8e72" + } + GradientStop { + position: 0.9 + color: "#1fc6a6" + } + GradientStop { + position: 0.91 + color: "#ea4c5f" } GradientStop { position: 1 - color: "#ff2d73" + color: "#ea4c5f" } } }