diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js
index 4ef523167c..fd0ff46d2d 100644
--- a/scripts/system/create/edit.js
+++ b/scripts/system/create/edit.js
@@ -4,7 +4,7 @@
// Persist toolbar by HRS on June 2nd, 2015.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
-// Copyright 2022-2024 Overte e.V.
+// Copyright 2022-2025 Overte e.V.
//
// This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing
//
@@ -544,7 +544,7 @@
localOnly: false
},
};
-
+ var fcreateNewEntity;
var toolBar = (function () {
var EDIT_SETTING = "io.highfidelity.isEditing"; // for communication with other scripts
var that = {},
@@ -554,7 +554,7 @@
dialogWindow = null,
tablet = null;
- function createNewEntity(requestedProperties) {
+ function createNewEntity(requestedProperties, entityHostType="domain") {
var dimensions = requestedProperties.dimensions ? requestedProperties.dimensions : DEFAULT_DIMENSIONS;
var position = createApp.getPositionToCreateEntity();
var entityID = null;
@@ -634,7 +634,7 @@
properties.visible = false;
}
- entityID = Entities.addEntity(properties);
+ entityID = Entities.addEntity(properties, entityHostType);
var dimensionsCheckCallback = function(){
// Adjust position of entity per bounding box after it has been created and auto-resized.
@@ -713,7 +713,9 @@
return entityID;
}
-
+
+ fcreateNewEntity = createNewEntity;
+
function closeExistingDialogWindow() {
if (dialogWindow) {
dialogWindow.close();
@@ -2611,6 +2613,10 @@
entity.properties.keyLight.direction = Vec3.toPolar(entity.properties.keyLight.direction);
entity.properties.keyLight.direction.z = 0.0;
}
+ if (selectionManager.selections.length === 1) {
+ entity.properties.children = createApp.getChildEntitiesList(entity.id);
+ }
+
selections.push(entity);
}
data.selections = selections;
@@ -2982,6 +2988,10 @@
type: 'importUi_LOAD_DATA',
importUiPersistedData: importUiPersistedData
});
+ } else if (data.type === "specificEntityNavigation") {
+ selectionManager.setSelections([data.id], this);
+ } else if (data.type === "createChildEntity") {
+ fcreateNewEntity(data.properties, data.entityHostType);
}
};
@@ -3388,7 +3398,7 @@
}
}
- createApp.rotateAsNextClickedSurface = function() {
+ createApp.rotateAsNextClickedSurface = function() {
if (!SelectionManager.hasSelection() || !SelectionManager.hasUnlockedSelection()) {
audioFeedback.rejection();
Window.notifyEditError("You have nothing selected, or the selection is locked.");
@@ -3398,4 +3408,19 @@
}
}
+ createApp.getChildEntitiesList = function(parentID) {
+ let children = Entities.getChildrenIDs(parentID);
+ let childList = [];
+ let i, properties;
+ if (children.length > 0) {
+ for (i = 0; i < children.length; i++ ) {
+ properties = Entities.getEntityProperties(children[i], ["id", "name", "type", "entityHostType"]);
+ if (properties.name !== undefined && properties.name !== entityShapeVisualizerSessionName) {
+ childList.push(properties);
+ }
+ }
+ }
+ return childList;
+ }
+
}()); // END LOCAL_SCOPE
diff --git a/scripts/system/create/entityProperties/html/entityProperties.html b/scripts/system/create/entityProperties/html/entityProperties.html
index 5fd7892fae..8d78d5c6f8 100644
--- a/scripts/system/create/entityProperties/html/entityProperties.html
+++ b/scripts/system/create/entityProperties/html/entityProperties.html
@@ -5,7 +5,7 @@
// Created by Ryan Huffman on 13 Nov 2014
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
-// Copyright 2022-2024 Overte e.V.
+// Copyright 2022-2025 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -20,6 +20,7 @@
+
@@ -62,6 +63,13 @@
+
";
+
+ renderer += "
";
+ renderer += "
";
+ renderer += "
URL: ";
+ renderer += "
";
+ renderer += "
";
+ renderer += "
";
+ renderer += "
";
+ renderer += "
";
+
+ document.getElementById("uiCreateChildEntityAssistant-form").innerHTML = renderer;
+}
+
+function selectTypeInChildEntityAssistant(type, entityHostType) {
+ if (type === "Model" || type === "Sound") { //Only if the url is cruxial. We want the less entry as possible for the user.
+ document.getElementById("nameCreateChildEntityAssistant").innerHTML = "
Create a child '" + type + "' entity ";
+ document.getElementById("typeSelectorCreateChildEntityAssistant").style.display = "none";
+ document.getElementById("paramaterCreateChildEntityAssistant").style.display = "block";
+ document.getElementById("createBtnCreateChildEntityAssistant").setAttribute("onclick","createChildEntity('" + type + "', '" + entityHostType + "')");
+ document.getElementById("cancelBtnCreateChildEntityAssistant").setAttribute("onclick","closeCreateChildEntityAssistant()");
+ } else {
+ createChildEntity(type, entityHostType);
+ }
+}
+
+function createChildEntity(type, entityHostType) {
+ let url = document.getElementById("urlCreateChildEntityAssistant").value;
+ let parentID = getPropertyInputElement("id").value;
+ let properties;
+ switch(type) {
+ case "Model":
+ properties = {
+ "type": type,
+ "modelURL": url,
+ "parentID": parentID,
+ "shapeType": "static-mesh",
+ "dynamic": false,
+ "grab": {"grabbable": false},
+ "useOriginalPivot": true
+ };
+ break;
+ case "Shape":
+ properties = {
+ "type": type,
+ "parentID": parentID,
+ "shape": "Cube"
+ };
+ break;
+ case "Text":
+ properties = {
+ "type": type,
+ "parentID": parentID
+ };
+ break;
+ case "Image":
+ properties = {
+ "type": type,
+ "parentID": parentID
+ };
+ break;
+ case "Web":
+ properties = {
+ "type": type,
+ "parentID": parentID
+ };
+ break;
+ case "ParticleEffect":
+ properties = {
+ "type": type,
+ "parentID": parentID
+ };
+ break;
+ case "ProceduralParticleEffect":
+ properties = {
+ "type": type,
+ "parentID": parentID
+ };
+ break;
+ case "Light":
+ properties = {
+ "type": type,
+ "parentID": parentID
+ };
+ break;
+ case "Zone":
+ properties = {
+ "type": type,
+ "parentID": parentID
+ };
+ break;
+ case "Material":
+ properties = {
+ "type": type,
+ "materialURL": "materialData",
+ "materialData": JSON.stringify({ "materials": {} }),
+ "parentID": parentID,
+ "priority": 1
+ };
+ break;
+ case "Sound":
+ properties = {
+ "type": type,
+ "soundURL": url,
+ "parentID": parentID
+ };
+ break;
+ case "PolyVox":
+ properties = {
+ "type": type,
+ "parentID": parentID
+ };
+ break;
+ }
+
+ EventBridge.emitWebEvent(JSON.stringify({
+ "type": "createChildEntity",
+ "properties": properties,
+ "entityHostType": entityHostType
+ }));
+
+ closeCreateChildEntityAssistant();
+}
+
+function closeCreateChildEntityAssistant() {
+ $('#uiCreateChildEntityAssistant').hide();
+ $('#properties-list').show();
+}
+
/**
* MATERIAL TARGET FUNCTIONS
*/
@@ -4925,15 +5204,21 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
selectedEntityIDs = new Set(selections.map(selection => selection.id));
const multipleSelections = currentSelections.length > 1;
const hasSelectedEntityChanged = !areSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs);
-
+
if (selections.length === 1) {
if (maSelectedId !== selections[0].id) {
closeMaterialAssistant();
}
maSelectedId = selections[0].id;
+ if (selections[0].properties.type === "Zone") {
+ skyboxColorForCopy = selections[0].properties.skybox.color;
+ } else {
+ skyboxColorForCopy = undefined;
+ }
} else {
closeMaterialAssistant();
maSelectedId = "";
+ skyboxColorForCopy = undefined;
}
requestZoneList();
@@ -4974,6 +5259,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
setCopyPastePositionAndRotationAvailability (selections.length, true);
disableProperties();
+
+ setParentIdNavigationAvailable(selections.length);
} else {
let entityHostType = selections[0].properties.entityHostType;
@@ -4984,6 +5271,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
}
if (hasSelectedEntityChanged) {
+ closeCreateChildEntityAssistant();
if (!multipleSelections) {
resetServerScriptStatus();
}
@@ -5006,6 +5294,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
disableProperties();
getPropertyInputElement('locked').removeAttribute('disabled');
setCopyPastePositionAndRotationAvailability (selections.length, true);
+ setParentIdNavigationAvailable(selections.length);
} else {
enableProperties();
disableSaveUserDataButton();
@@ -5013,6 +5302,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
disableSaveParticleUpdateDataButton();
disableSaveParticleRenderDataButton();
setCopyPastePositionAndRotationAvailability (selections.length, false);
+ setParentIdNavigationAvailable(selections.length);
}
Object.entries(properties).forEach(function([propertyID, property]) {
@@ -5037,7 +5327,9 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
const isSubProperty = propertyData.subPropertyOf !== undefined;
if (propertyValue === undefined && !isMultiDiffValue && !isSubProperty) {
- return;
+ if (propertyData.type !== "childList") {
+ return;
+ }
}
if (!shownGroups.includes(property.group_id)) {
@@ -5198,6 +5490,14 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
}
break;
}
+ case 'childList': {
+ let parentID = selections[0].properties.parentID;
+ if (selections.length !== 1 || parentID === UUID_NONE) {
+ parentID = "";
+ }
+ setChildListData(property.elInput, propertyValue, parentID, entityHostType);
+ break;
+ }
case 'icon': {
property.elSpan.innerHTML = propertyData.icons[propertyValue];
property.elSpan.style.display = "inline-block";
@@ -5641,6 +5941,9 @@ function loaded() {
elScript.parentNode.className = "url refresh";
elServerScripts.parentNode.className = "url refresh";
+ let elParentID = getPropertyInputElement("parentID");
+ elParentID.parentNode.className = "url refresh";
+
// User Data
let userDataProperty = properties["userData"];
let elUserData = userDataProperty.elInput;
diff --git a/scripts/system/create/entityProperties/html/tabs/children.png b/scripts/system/create/entityProperties/html/tabs/children.png
new file mode 100644
index 0000000000..8bafc9ba29
Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/children.png differ
diff --git a/scripts/system/html/css/createChildEntityAssistant.css b/scripts/system/html/css/createChildEntityAssistant.css
new file mode 100644
index 0000000000..ff3a8984f4
--- /dev/null
+++ b/scripts/system/html/css/createChildEntityAssistant.css
@@ -0,0 +1,175 @@
+/*
+// createChildEntityAssistant.css
+//
+// Created by Alezia Kurdis on March 3rd, 2025.
+// Copyright 2025 Overte e.V.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+*/
+
+#uiCreateChildEntityAssistant {
+ position: fixed;
+ display: none;
+ width: 100%;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+ bottom: 0px;
+ border: 0px;
+ padding: 0px;
+ background-color: #404040;
+ z-index: 2;
+ border-collapse: collapse;
+}
+
+#uiCreateChildEntityAssistant-scrollable {
+ border-collapse: collapse;
+ overflow-y: scroll;
+ height: 100%;
+}
+
+#uiCreateChildEntityAssistant-headerContainer {
+ width: 98%;
+ text-align: right;
+ padding: 6px;
+ display: flex;
+}
+
+#uiCreateChildEntityAssistant-closeButton {
+ font-family: Raleway-Bold;
+ font-size: 18px;
+ text-align: center;
+ border: 0px;
+ padding: 6px;
+ color: #FFFFFF;
+}
+
+font.uiCreateChildEntityAssistant-Explain{
+ background-color: #2E2E2E;
+ font-family: Raleway-Regular;
+ text-decoration: Italic;
+ font-size: 10px;
+ color: #8ad5ff;
+}
+
+font.uiCreateChildEntityAssistant-label{
+ background-color: #2E2E2E;
+ font-family: Raleway-SemiBold;
+ text-decoration: none;
+ font-size: 12px;
+ color: #D2D2D2;
+}
+
+label.uiCreateChildEntityAssistant-label{
+ background-color: #2E2E2E;
+ font-family: Raleway-SemiBold;
+ text-decoration: none;
+ font-size: 12px;
+ color: #D2D2D2;
+}
+
+font.uiCreateChildEntityAssistant-title{
+ background-color: #404040;
+ font-family: Raleway-Bold;
+ font-size: 18px;
+ text-decoration: none;
+ color: #F2F2F2;
+ white-space: normal;
+}
+
+#urlCreateChildEntityAssistant {
+ background-color: #222222;
+ color: #bbbbbb;
+ width: 90%;
+}
+
+#childEntityActionsContainer {
+ width: 100%;
+ text-align: center;
+ padding: 3px;
+}
+
+#viewParentContainer {
+ display: inline-block;
+ text-align: left;
+ width: 50%;
+ vertical-align: middle;
+}
+
+#addChildEntityContainer {
+ display: inline-block;
+ width: 50%;
+ text-align: right;
+ vertical-align: middle;
+}
+
+span.tileCreateChildEntityAssistant-domain {
+ border: 2px solid #000000;
+ background-color: #000000;
+ padding: 5px;
+ margin: 10px;
+ display: inline-block;
+ text-align: center;
+ border-radius: 10px;
+ width: 80px;
+ height: 80px;
+ color: #FFFFFF;
+ font-family: FiraSans-SemiBold;
+ font-size: 13px;
+}
+
+span.tileCreateChildEntityAssistant-domain:hover {
+ border: 2px solid #FFFFFF;
+}
+
+span.tileCreateChildEntityAssistant-avatar {
+ border: 2px solid #000000;
+ background-color: #000000;
+ padding: 5px;
+ margin: 10px;
+ display: inline-block;
+ text-align: center;
+ align-items: center;
+ border-radius: 10px;
+ width: 80px;
+ height: 80px;
+ color: #7fdb98;
+ font-family: FiraSans-SemiBold;
+ font-size: 13px;
+}
+
+span.tileCreateChildEntityAssistant-avatar:hover {
+ border: 2px solid #7fdb98;
+}
+
+span.viewChildProperties {
+ width: 100%;
+ padding-left: 12px;
+ padding-right: 12px;
+ text-align: center;
+}
+
+font.iconCreateChildEntityAssistant {
+ font-family: HiFi-Glyphs;
+ font-size: 50px;
+}
+
+#typeSelectorCreateChildEntityAssistant {
+ width: 100%;
+ text-align: center;
+}
+
+font.addChildEntity-domain {
+ color: #FFFFFF;
+}
+
+font.addChildEntity-avatar {
+ color: #7fdb98;
+}
+
+#paramaterCreateChildEntityAssistant {
+ font-family: FiraSans-SemiBold;
+ font-size: 14px;
+ padding: 10px;
+}
diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css
index da7349aac8..7658e2fb24 100644
--- a/scripts/system/html/css/edit-style.css
+++ b/scripts/system/html/css/edit-style.css
@@ -175,10 +175,21 @@ tr:focus {
outline: none;
}
+th.childrenTableHeader {
+ height: 26px;
+ background-color: #1c1c1c;
+ border-right: 1px solid #575757;
+ border-bottom: 1px solid #575757;
+}
+
tr.avatarEntity {
color: #7fdb98;
}
+tr.localEntity {
+ color: #f0d769;
+}
+
tr.selAvatarEntity {
color: #000000;
background-color: #7fdb98;
@@ -468,6 +479,18 @@ input[type=button].glyph, button.hifi-edit-button.glyph {
padding: 0;
}
+input[type=button].navigation, button.hifi-edit-button.navigation {
+ font-family: HiFi-Glyphs;
+ font-size: 20px;
+ text-transform: none;
+ min-width: 32px;
+ padding: 0;
+}
+
+input[type=button].navigation:focus, button.hifi-edit-button.navigation:focus {
+ outline: none;
+}
+
input[type=button].normal, button.hifi-edit-button.normal {
font-family: FiraSans-SemiBold;
font-size: 15px;
@@ -2259,3 +2282,13 @@ div.menu-item-caption {
div.menu-item-shortcut {
float: right;
}
+
+span.viewParent {
+ color: #ffffff;
+}
+
+font.viewParentIcon {
+ font-family: HiFi-Glyphs;
+ font-weight: bold;
+}
+