diff --git a/interface/resources/fonts/vircadia_glyphs.ttf b/interface/resources/fonts/vircadia_glyphs.ttf
new file mode 100644
index 0000000000..b6a8c2e7ad
Binary files /dev/null and b/interface/resources/fonts/vircadia_glyphs.ttf differ
diff --git a/scripts/system/create/audioFeedback/audioFeedback.js b/scripts/system/create/audioFeedback/audioFeedback.js
new file mode 100644
index 0000000000..f1900d5716
--- /dev/null
+++ b/scripts/system/create/audioFeedback/audioFeedback.js
@@ -0,0 +1,34 @@
+//
+// audioFeedback.js
+//
+// Created by Alezia Kurdis on September 30, 2020.
+// Copyright 2020 Vircadia contributors.
+//
+// This script add audio feedback (confirmation and rejection) for user interactions that require one.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+audioFeedback = (function() {
+ var that = {};
+
+ var confirmationSound = SoundCache.getSound(Script.resolvePath("./sounds/confirmation.mp3"));
+ var rejectionSound = SoundCache.getSound(Script.resolvePath("./sounds/rejection.mp3"));
+
+ that.confirmation = function() { //Play a confirmation sound
+ var injector = Audio.playSound(confirmationSound, {
+ "volume": 0.3,
+ "localOnly": true
+ });
+ }
+
+ that.rejection = function() { //Play a rejection sound
+ var injector = Audio.playSound(rejectionSound, {
+ "volume": 0.3,
+ "localOnly": true
+ });
+ }
+
+ return that;
+})();
diff --git a/scripts/system/create/audioFeedback/sounds/confirmation.mp3 b/scripts/system/create/audioFeedback/sounds/confirmation.mp3
new file mode 100644
index 0000000000..39143fce4f
Binary files /dev/null and b/scripts/system/create/audioFeedback/sounds/confirmation.mp3 differ
diff --git a/scripts/system/create/audioFeedback/sounds/rejection.mp3 b/scripts/system/create/audioFeedback/sounds/rejection.mp3
new file mode 100644
index 0000000000..46e564dcdc
Binary files /dev/null and b/scripts/system/create/audioFeedback/sounds/rejection.mp3 differ
diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js
index b8f1dc8014..5c488a71ee 100644
--- a/scripts/system/create/edit.js
+++ b/scripts/system/create/edit.js
@@ -34,7 +34,8 @@ Script.include([
"../libraries/entityIconOverlayManager.js",
"../libraries/gridTool.js",
"entityList/entityList.js",
- "entitySelectionTool/entitySelectionTool.js"
+ "entitySelectionTool/entitySelectionTool.js",
+ "audioFeedback/audioFeedback.js"
]);
var CreateWindow = Script.require('./modules/createWindow.js');
@@ -104,6 +105,8 @@ var entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleE
}
});
+var hmdMultiSelectMode = false;
+
var cameraManager = new CameraManager();
var grid = new Grid();
@@ -824,7 +827,7 @@ var toolBar = (function () {
HMD.displayModeChanged.connect(function() {
if (isActive) {
- tablet.gotoHomeScreen();
+ tablet.gotoHomeScreen();
}
that.setActive(false);
});
@@ -1131,7 +1134,11 @@ function handleOverlaySelectionToolUpdates(channel, message, sender) {
var entity = entityIconOverlayManager.findEntity(data.overlayID);
if (entity !== null) {
- selectionManager.setSelections([entity], this);
+ if (hmdMultiSelectMode) {
+ selectionManager.addEntity(entity, true, this);
+ } else {
+ selectionManager.setSelections([entity], this);
+ }
}
}
}
@@ -1694,6 +1701,7 @@ function unparentSelectedEntities() {
var parentCheck = false;
if (selectedEntities.length < 1) {
+ audioFeedback.rejection();
Window.notifyEditError("You must have an entity selected in order to unparent it.");
return;
}
@@ -1706,12 +1714,14 @@ function unparentSelectedEntities() {
return true;
});
if (parentCheck) {
+ audioFeedback.confirmation();
if (selectedEntities.length > 1) {
Window.notify("Entities unparented");
} else {
Window.notify("Entity unparented");
}
} else {
+ audioFeedback.rejection();
if (selectedEntities.length > 1) {
Window.notify("Selected Entities have no parents");
} else {
@@ -1719,6 +1729,7 @@ function unparentSelectedEntities() {
}
}
} else {
+ audioFeedback.rejection();
Window.notifyEditError("You have nothing selected to unparent");
}
}
@@ -1726,6 +1737,7 @@ function parentSelectedEntities() {
if (SelectionManager.hasSelection()) {
var selectedEntities = selectionManager.selections;
if (selectedEntities.length <= 1) {
+ audioFeedback.rejection();
Window.notifyEditError("You must have multiple entities selected in order to parent them");
return;
}
@@ -1742,11 +1754,14 @@ function parentSelectedEntities() {
});
if (parentCheck) {
+ audioFeedback.confirmation();
Window.notify("Entities parented");
} else {
+ audioFeedback.rejection();
Window.notify("Entities are already parented to last");
}
} else {
+ audioFeedback.rejection();
Window.notifyEditError("You have nothing selected to parent");
}
}
@@ -2339,6 +2354,15 @@ var PropertiesTool = function (opts) {
};
function updateSelections(selectionUpdated, caller) {
+ if (HMD.active && visible) {
+ webView.setLandscape(true);
+ } else {
+ if (!visible) {
+ hmdMultiSelectMode = false;
+ webView.setLandscape(false);
+ }
+ }
+
if (blockPropertyUpdates) {
return;
}
diff --git a/scripts/system/create/entityList/entityList.js b/scripts/system/create/entityList/entityList.js
index b68dcf80ba..4b8163968d 100644
--- a/scripts/system/create/entityList/entityList.js
+++ b/scripts/system/create/entityList/entityList.js
@@ -3,6 +3,7 @@
// entityList.js
//
// Copyright 2014 High Fidelity, Inc.
+// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -14,6 +15,7 @@
var PROFILING_ENABLED = false;
var profileIndent = '';
+
const PROFILE_NOOP = function(_name, fn, args) {
fn.apply(this, args);
};
@@ -73,7 +75,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
that.setVisible = function(newVisible) {
visible = newVisible;
webView.setVisible(shouldUseEditTabletApp() && visible);
- entityListWindow.setVisible(!shouldUseEditTabletApp() && visible);
+ entityListWindow.setVisible(!shouldUseEditTabletApp() && visible);
};
that.isVisible = function() {
@@ -163,6 +165,15 @@ EntityListTool = function(shouldUseEditTabletApp) {
}
that.sendUpdate = function() {
+ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+ if (HMD.active) {
+ tablet.setLandscape(true);
+ }
+ emitJSONScriptEvent({
+ "type": "confirmHMDstate",
+ "isHmd": HMD.active
+ });
+
PROFILE('Script-sendUpdate', function() {
var entities = [];
@@ -302,6 +313,16 @@ EntityListTool = function(shouldUseEditTabletApp) {
SelectionDisplay.toggleSpaceMode();
} else if (data.type === 'keyUpEvent') {
keyUpEventFromUIWindow(data.keyUpEvent);
+ } else if (data.type === 'undo') {
+ undoHistory.undo();
+ } else if (data.type === 'redo') {
+ undoHistory.redo();
+ } else if (data.type === 'parent') {
+ parentSelectedEntities();
+ } else if (data.type === 'unparent') {
+ unparentSelectedEntities();
+ } else if (data.type === 'hmdMultiSelectMode') {
+ hmdMultiSelectMode = data.value;
}
};
diff --git a/scripts/system/create/entityList/html/entityList.html b/scripts/system/create/entityList/html/entityList.html
index b7ff7cd4e4..824ca380ec 100644
--- a/scripts/system/create/entityList/html/entityList.html
+++ b/scripts/system/create/entityList/html/entityList.html
@@ -3,6 +3,7 @@
//
// Created by Ryan Huffman on 19 Nov 2014
// Copyright 2014 High Fidelity, Inc.
+// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -30,6 +31,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/system/create/entityList/html/js/entityList.js b/scripts/system/create/entityList/html/js/entityList.js
index b70e53ce15..aa40d5286f 100644
--- a/scripts/system/create/entityList/html/js/entityList.js
+++ b/scripts/system/create/entityList/html/js/entityList.js
@@ -2,6 +2,7 @@
//
// Created by Ryan Huffman on 19 Nov 2014
// Copyright 2014 High Fidelity, Inc.
+// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -164,6 +165,7 @@ let selectedEntities = [];
let entityList = null; // The ListView
+let hmdMultiSelectMode = false;
/**
* @type EntityListContextMenu
*/
@@ -198,6 +200,15 @@ let elEntityTable,
elRefresh,
elToggleLocked,
elToggleVisible,
+ elHmdMultiSelect,
+ elHmdCopy,
+ elHmdCut,
+ elHmdPaste,
+ elHmdDuplicate,
+ elUndo,
+ elRedo,
+ elParent,
+ elUnparent,
elDelete,
elFilterTypeMultiselectBox,
elFilterTypeText,
@@ -233,7 +244,7 @@ const PROFILE = !ENABLE_PROFILING ? PROFILE_NOOP : function(name, fn, args) {
console.log("PROFILE-Web " + profileIndent + "(" + name + ") End " + delta + "ms");
};
-function loaded() {
+function loaded() {
openEventBridge(function() {
elEntityTable = document.getElementById("entity-table");
elEntityTableHeader = document.getElementById("entity-table-header");
@@ -242,6 +253,15 @@ function loaded() {
elRefresh = document.getElementById("refresh");
elToggleLocked = document.getElementById("locked");
elToggleVisible = document.getElementById("visible");
+ elHmdMultiSelect = document.getElementById("hmdmultiselect");
+ elHmdCopy = document.getElementById("hmdcopy");
+ elHmdCut = document.getElementById("hmdcut");
+ elHmdPaste = document.getElementById("hmdpaste");
+ elHmdDuplicate = document.getElementById("hmdduplicate");
+ elUndo = document.getElementById("undo");
+ elRedo = document.getElementById("redo");
+ elParent = document.getElementById("parent");
+ elUnparent = document.getElementById("unparent");
elDelete = document.getElementById("delete");
elFilterTypeMultiselectBox = document.getElementById("filter-type-multiselect-box");
elFilterTypeText = document.getElementById("filter-type-text");
@@ -270,6 +290,40 @@ function loaded() {
elExport.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'export'}));
};
+ elHmdMultiSelect.onclick = function() {
+ if (hmdMultiSelectMode) {
+ elHmdMultiSelect.className = "vglyph";
+ hmdMultiSelectMode = false;
+ } else {
+ elHmdMultiSelect.className = "white vglyph";
+ hmdMultiSelectMode = true;
+ }
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'hmdMultiSelectMode', value: hmdMultiSelectMode }));
+ };
+ elHmdCopy.onclick = function() {
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'copy' }));
+ };
+ elHmdCut.onclick = function() {
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'cut' }));
+ };
+ elHmdPaste.onclick = function() {
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'paste' }));
+ };
+ elHmdDuplicate.onclick = function() {
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'duplicate' }));
+ };
+ elParent.onclick = function() {
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'parent' }));
+ };
+ elUnparent.onclick = function() {
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'unparent' }));
+ };
+ elUndo.onclick = function() {
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'undo' }));
+ };
+ elRedo.onclick = function() {
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'redo' }));
+ };
elDelete.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
};
@@ -538,7 +592,7 @@ function loaded() {
let selection = [entityID];
let controlKey = window.navigator.platform.startsWith("Mac") ? clickEvent.metaKey : clickEvent.ctrlKey;
- if (controlKey) {
+ if (controlKey || hmdMultiSelectMode) {
let selectedIndex = selectedEntities.indexOf(entityID);
if (selectedIndex >= 0) {
selection = [];
@@ -1364,10 +1418,12 @@ function loaded() {
}
}));
}, false);
-
+
if (window.EventBridge !== undefined) {
EventBridge.scriptEventReceived.connect(function(data) {
+
data = JSON.parse(data);
+
if (data.type === "clearEntityList") {
clearEntities();
} else if (data.type === "selectionUpdate") {
@@ -1395,6 +1451,20 @@ function loaded() {
removeEntities(data.ids);
} else if (data.type === "setSpaceMode") {
setSpaceMode(data.spaceMode);
+ } else if (data.type === "confirmHMDstate") {
+ if (data.isHmd) {
+ document.getElementById("hmdmultiselect").style.display = "inline";
+ document.getElementById("hmdcopy").style.display = "inline";
+ document.getElementById("hmdcut").style.display = "inline";
+ document.getElementById("hmdpaste").style.display = "inline";
+ document.getElementById("hmdduplicate").style.display = "inline";
+ } else {
+ document.getElementById("hmdmultiselect").style.display = "none";
+ document.getElementById("hmdcopy").style.display = "none";
+ document.getElementById("hmdcut").style.display = "none";
+ document.getElementById("hmdpaste").style.display = "none";
+ document.getElementById("hmdduplicate").style.display = "none";
+ }
}
});
}
diff --git a/scripts/system/create/entitySelectionTool/entitySelectionTool.js b/scripts/system/create/entitySelectionTool/entitySelectionTool.js
index 33f9bbafb3..0250ead0a9 100644
--- a/scripts/system/create/entitySelectionTool/entitySelectionTool.js
+++ b/scripts/system/create/entitySelectionTool/entitySelectionTool.js
@@ -103,7 +103,11 @@ SelectionManager = (function() {
if (wantDebug) {
print("setting selection to " + messageParsed.entityID);
}
- that.setSelections([messageParsed.entityID], that);
+ if (hmdMultiSelectMode) {
+ that.addEntity(messageParsed.entityID, true, that);
+ } else {
+ that.setSelections([messageParsed.entityID], that);
+ }
}
} else if (messageParsed.method === "clearSelection") {
if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) {
@@ -314,6 +318,7 @@ SelectionManager = (function() {
that.addChildrenEntities(originalEntityID, entitiesToDuplicate, entityHostTypes[i].entityHostType);
}
+ var duplicateInterrupted = false;
// duplicate entities from above and store their original to new entity mappings and children needing re-parenting
for (var i = 0; i < entitiesToDuplicate.length; i++) {
var originalEntityID = entitiesToDuplicate[i];
@@ -360,6 +365,8 @@ SelectionManager = (function() {
duplicatedChildrenWithOldParents[newEntityID] = properties.parentID;
}
originalEntityToNewEntityID[originalEntityID] = newEntityID;
+ } else {
+ duplicateInterrupted = true;
}
}
@@ -378,6 +385,11 @@ SelectionManager = (function() {
}
});
+ if (duplicateInterrupted) {
+ audioFeedback.rejection();
+ } else {
+ audioFeedback.confirmation();
+ }
return duplicatedEntityIDs;
};
diff --git a/scripts/system/create/qml/EditTabView.qml b/scripts/system/create/qml/EditTabView.qml
index a0cff70d50..53f6068424 100644
--- a/scripts/system/create/qml/EditTabView.qml
+++ b/scripts/system/create/qml/EditTabView.qml
@@ -55,18 +55,18 @@ TabBar {
font.pixelSize: 14
font.bold: true
anchors.top: parent.top
- anchors.topMargin: 28
+ anchors.topMargin: 30
anchors.left: parent.left
- anchors.leftMargin: 28
+ anchors.leftMargin: 30
}
Flow {
id: createEntitiesFlow
- spacing: 35
+ spacing: 20
anchors.right: parent.right
- anchors.rightMargin: 55
+ anchors.rightMargin: 30
anchors.left: parent.left
- anchors.leftMargin: 55
+ anchors.leftMargin: 30
anchors.top: parent.top
anchors.topMargin: 70
@@ -186,9 +186,9 @@ TabBar {
color: hifi.buttons.black
colorScheme: hifi.colorSchemes.dark
anchors.right: parent.right
- anchors.rightMargin: 55
+ anchors.rightMargin: 30
anchors.left: parent.left
- anchors.leftMargin: 55
+ anchors.leftMargin: 30
anchors.top: createEntitiesFlow.bottom
anchors.topMargin: 35
onClicked: {
@@ -205,9 +205,9 @@ TabBar {
color: hifi.buttons.black
colorScheme: hifi.colorSchemes.dark
anchors.right: parent.right
- anchors.rightMargin: 55
+ anchors.rightMargin: 30
anchors.left: parent.left
- anchors.leftMargin: 55
+ anchors.leftMargin: 30
anchors.top: assetServerButton.bottom
anchors.topMargin: 20
onClicked: {
diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css
index ada8116a0d..8a381ff4ad 100644
--- a/scripts/system/html/css/edit-style.css
+++ b/scripts/system/html/css/edit-style.css
@@ -65,6 +65,14 @@
url(../fonts/hifi-glyphs.ttf);
}
+@font-face {
+ font-family: Vircadia-Glyphs;
+ src: url(../../../../resources/fonts/vircadia_glyphs.ttf),
+ url(../../../../fonts/vircadia_glyphs.ttf),
+ url(../../../../interface/resources/fonts/vircadia_glyphs.ttf),
+ url(../fonts/vircadia_glyphs.ttf);
+}
+
* {
margin: 0;
padding: 0;
@@ -407,6 +415,14 @@ input[type=button].glyph, button.hifi-edit-button.glyph {
padding: 0;
}
+input[type=button].vglyph, button.hifi-edit-button.vglyph {
+ font-family: Vircadia-Glyphs;
+ font-size: 20px;
+ text-transform: none;
+ min-width: 32px;
+ padding: 0;
+}
+
input[type=button].red, button.hifi-edit-button.red {
color: #fff;
background-color: #94132e;
@@ -417,6 +433,16 @@ input[type=button].blue, button.hifi-edit-button.blue {
background-color: #1080b8;
background: linear-gradient(#00b4ef 20%, #1080b8 100%);
}
+input[type=button].orange, button.hifi-edit-button.orange {
+ color: #fff;
+ background-color: #8f5100;
+ background: linear-gradient(#d97b00 20%, #8f5100 100%);
+}
+input[type=button].green, button.hifi-edit-button.green {
+ color: #fff;
+ background-color: #078a00;
+ background: linear-gradient(#00cc07 20%, #078a00 100%);
+}
input[type=button].white, button.hifi-edit-button.white {
color: #121212;
background-color: #afafaf;
@@ -435,6 +461,14 @@ input[type=button].blue:enabled:hover, button.hifi-edit-button.blue:enabled:hove
background: linear-gradient(#00b4ef, #00b4ef);
border: none;
}
+input[type=button].orange:enabled:hover, button.hifi-edit-button.orange:enabled:hover {
+ background: linear-gradient(#d97b00, #d97b00);
+ border: none;
+}
+input[type=button].green:enabled:hover, button.hifi-edit-button.green:enabled:hover {
+ background: linear-gradient(#00cc07, #00cc07);
+ border: none;
+}
input[type=button].white:enabled:hover, button.hifi-edit-button.white:enabled:hover {
background: linear-gradient(#fff, #fff);
border: none;
@@ -449,6 +483,12 @@ input[type=button].red:active, button.hifi-edit-button.red:active {
input[type=button].blue:active, button.hifi-edit-button.blue:active {
background: linear-gradient(#1080b8, #1080b8);
}
+input[type=button].orange:active, button.hifi-edit-button.orange:active {
+ background: linear-gradient(#8f5100, #8f5100);
+}
+input[type=button].green:active, button.hifi-edit-button.green:active {
+ background: linear-gradient(#078a00, #078a00);
+}
input[type=button].white:active, button.hifi-edit-button.white:active {
background: linear-gradient(#afafaf, #afafaf);
}