From 4f4ec064e4bf7bfa8dc3b846e50c0a52a24ef7a7 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 Apr 2019 15:43:04 -0700 Subject: [PATCH] add dynamic multiselect for material targets --- scripts/system/edit.js | 14 +- scripts/system/html/js/entityProperties.js | 260 ++++++++++++++++----- 2 files changed, 209 insertions(+), 65 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 7c5af27b82..8ca6c5b14c 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2522,7 +2522,19 @@ var PropertiesTool = function (opts) { type: 'propertyRangeReply', propertyRanges: propertyRanges, }); - } + } else if (data.type === "materialTargetRequest") { + var properties = Entities.getEntityProperties(data.entityID, ["type", "parentID"]); + var parentModel = properties.parentID !== Uuid.NULL && + Entities.getEntityProperties(properties.parentID, ["type"]).type === "Model"; + var parentModelData; + if (properties.type === "Material" && parentModel) { + parentModelData = Graphics.getModel(properties.parentID); + } + emitScriptEvent({ + type: 'materialTargetReply', + materialTargetData: parentModelData, + }); + } }; HMD.displayModeChanged.connect(function() { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index f450ff2036..f2bff12f53 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -650,23 +650,10 @@ const GROUPS = [ propertyID: "materialData", }, { - label: "Select Submesh", - type: "bool", - propertyID: "selectSubmesh", - }, - { - label: "Submesh to Replace", - type: "number-draggable", - min: 0, - step: 1, - propertyID: "submeshToReplace", - indentedLabel: true, - }, - { - label: "Material to Replace", - type: "string", - propertyID: "materialNameToReplace", - indentedLabel: true, + label: "Material Target", + type: "dynamic-multiselect", + propertyUpdate: materialTargetPropertyUpdate, + propertyID: "parentMaterialName", }, { label: "Priority", @@ -1588,6 +1575,8 @@ function getPropertyInputElement(propertyID) { return { red: property.elNumberR.elInput, green: property.elNumberG.elInput, blue: property.elNumberB.elInput }; case 'icon': return property.elLabel; + case 'dynamic-multiselect': + return property.elDivOptions; default: return undefined; } @@ -1710,6 +1699,10 @@ function resetProperties() { property.elInput.imageLoad(property.elInput.value); break; } + case 'dynamic-multiselect': { + resetDynamicMultiselectProperty(property.elDivOptions); + break; + } } let showPropertyRules = properties[propertyID].showPropertyRules; @@ -2386,7 +2379,7 @@ function createTextureProperty(property, elProperty) { return elResult; } -function createButtonsProperty(property, elProperty, elLabel) { +function createButtonsProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; @@ -2399,6 +2392,42 @@ function createButtonsProperty(property, elProperty, elLabel) { return elProperty; } +function createDynamicMultiselectProperty(property, elProperty) { + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "dynamic-multiselect"; + + let elDivOptions = document.createElement('div'); + elDivOptions.setAttribute("id", elementID + "-options"); + + let elDivButtons = document.createElement('div'); + elDivButtons.setAttribute("id", elDivOptions.getAttribute("id") + "-buttons"); + + let elLabel = document.createElement('label'); + elLabel.innerText = "No Options"; + elDivOptions.appendChild(elLabel); + + let buttons = [ { id: "selectAll", label: "Select All", className: "black", onClick: selectAllMaterialTarget }, + { id: "clearAll", label: "Clear All", className: "black", onClick: clearAllMaterialTarget } ]; + addButtons(elDivButtons, elementID, buttons, false); + + elProperty.appendChild(elDivOptions); + elProperty.appendChild(elDivButtons); + + return elDivOptions; +} + +function resetDynamicMultiselectProperty(elDivOptions) { + let elInputs = elDivOptions.getElementsByTagName("input"); + while (elInputs.length > 0) { + let elDivOption = elInputs[0].parentNode; + elDivOption.parentNode.removeChild(elDivOption); + } + elDivOptions.firstChild.style.display = "none"; + elDivOptions.parentNode.lastChild.style.display = "block"; +} + function createTupleNumberInput(property, subLabel) { let propertyElementID = property.elementID; let propertyData = property.data; @@ -2522,6 +2551,10 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI property.elProperty = createButtonsProperty(property, elProperty); break; } + case 'dynamic-multiselect': { + property.elDivOptions = createDynamicMultiselectProperty(property, elProperty); + break; + } case 'placeholder': case 'sub-header': { break; @@ -3058,7 +3091,7 @@ function setDropdownValue(event) { /** - * TEXTAREA / PARENT MATERIAL NAME FUNCTIONS + * TEXTAREA FUNCTIONS */ function setTextareaScrolling(element) { @@ -3066,16 +3099,139 @@ function setTextareaScrolling(element) { element.setAttribute("scrolling", isScrolling ? "true" : "false"); } -function showParentMaterialNameBox(number, elNumber, elString) { - if (number) { - $('#property-submeshToReplace').parent().show(); - $('#property-materialNameToReplace').parent().hide(); - elString.value = ""; - } else { - $('#property-materialNameToReplace').parent().show(); - $('#property-submeshToReplace').parent().hide(); - elNumber.value = 0; + +/** + * MATERIAL TARGET FUNCTIONS + */ + +function setMaterialTargetData(materialTargetData) { + let elDivOptions = getPropertyInputElement("parentMaterialName"); + resetDynamicMultiselectProperty(elDivOptions); + + if (materialTargetData === undefined) { + elDivOptions.firstChild.style.display = "block"; + elDivOptions.parentNode.lastChild.style.display = "none"; + return; } + + let numMeshes = materialTargetData.numMeshes; + for (let i = 0; i < numMeshes; ++i) { + addMaterialTarget(elDivOptions, i, false); + } + + let materialNames = materialTargetData.materialNames; + let materialNamesAdded = []; + for (let i = 0; i < materialNames.length; ++i) { + let materialName = materialNames[i]; + if (materialNamesAdded.indexOf(materialName) === -1) { + addMaterialTarget(elDivOptions, materialName, true); + materialNamesAdded.push(materialName); + } + } + + materialTargetPropertyUpdate(elDivOptions.propertyValue); +} + +function addMaterialTarget(elDivOptions, targetID, isMaterialName) { + let elementID = elDivOptions.getAttribute("id"); + elementID += isMaterialName ? "-material-" : "-mesh-"; + elementID += targetID; + + let elDiv = document.createElement('div'); + elDiv.className = "materialTargetDiv"; + elDiv.onclick = onToggleMaterialTarget; + elDivOptions.appendChild(elDiv); + + let elInput = document.createElement('input'); + elInput.className = "materialTargetInput"; + elInput.setAttribute("type", "checkbox"); + elInput.setAttribute("id", elementID); + elInput.setAttribute("targetID", targetID); + elInput.setAttribute("isMaterialName", isMaterialName); + elDiv.appendChild(elInput); + + let elLabel = document.createElement('label'); + elLabel.setAttribute("for", elementID); + elLabel.innerText = isMaterialName ? "Material " + targetID : "Mesh Index " + targetID; + elDiv.appendChild(elLabel); + + return elDiv; +} + +function onToggleMaterialTarget(event) { + let elTarget = event.target; + if (elTarget instanceof HTMLInputElement) { + sendMaterialTargetProperty(); + } + event.stopPropagation(); +} + +function setAllMaterialTargetInputs(checked) { + let elDivOptions = getPropertyInputElement("parentMaterialName"); + let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); + for (let i = 0; i < elInputs.length; ++i) { + elInputs[i].checked = checked; + } +} + +function selectAllMaterialTarget() { + setAllMaterialTargetInputs(true); + sendMaterialTargetProperty(); +} + +function clearAllMaterialTarget() { + setAllMaterialTargetInputs(false); + sendMaterialTargetProperty(); +} + +function sendMaterialTargetProperty() { + let elDivOptions = getPropertyInputElement("parentMaterialName"); + let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); + + let materialTargetList = ""; + for (let i = 0; i < elInputs.length; ++i) { + let elInput = elInputs[i]; + if (elInput.checked) { + let targetID = elInput.getAttribute("targetID"); + if (elInput.getAttribute("isMaterialName") === "true") { + materialTargetList += "mat::" + targetID + ","; + } else { + materialTargetList += targetID + ","; + } + } + } + + if (materialTargetList !== "") { + materialTargetList = materialTargetList.substring(0, materialTargetList.length - 1); + materialTargetList = "[" + materialTargetList + "]"; + } + + updateProperty("parentMaterialName", materialTargetList, false); +} + +function materialTargetPropertyUpdate(propertyValue) { + let elDivOptions = getPropertyInputElement("parentMaterialName"); + let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); + + if (propertyValue.charAt(0) === '[') { + propertyValue = propertyValue.substring(1, propertyValue.length); + } + if (propertyValue.charAt(propertyValue.length - 1) === ']') { + propertyValue = propertyValue.substring(0, propertyValue.length - 1); + } + + let materialTargets = propertyValue.split(","); + for (let i = 0; i < elInputs.length; ++i) { + let elInput = elInputs[i]; + let targetID = elInput.getAttribute("targetID"); + let materialTargetName = targetID; + if (elInput.getAttribute("isMaterialName") === "true") { + materialTargetName = "mat::" + targetID; + } + elInput.checked = materialTargets.indexOf(materialTargetName) >= 0; + } + + elDivOptions.propertyValue = propertyValue; } @@ -3504,6 +3660,12 @@ function loaded() { property.elInput.imageLoad(property.elInput.value); break; } + case 'dynamic-multiselect': { + if (property.data.propertyUpdate) { + property.data.propertyUpdate(propertyValue); + } + break; + } } let showPropertyRules = property.showPropertyRules; @@ -3517,22 +3679,6 @@ function loaded() { } updateVisibleSpaceModeProperties(); - - if (selectedEntityProperties.type === "Material") { - let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); - let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); - let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); - let parentMaterialName = selectedEntityProperties.parentMaterialName; - if (parentMaterialName.startsWith(MATERIAL_PREFIX_STRING)) { - elParentMaterialNameString.value = parentMaterialName.replace(MATERIAL_PREFIX_STRING, ""); - showParentMaterialNameBox(false, elParentMaterialNameNumber, elParentMaterialNameString); - elParentMaterialNameCheckbox.checked = false; - } else { - elParentMaterialNameNumber.value = parseInt(parentMaterialName); - showParentMaterialNameBox(true, elParentMaterialNameNumber, elParentMaterialNameString); - elParentMaterialNameCheckbox.checked = true; - } - } let json = null; try { @@ -3580,6 +3726,10 @@ function loaded() { hideMaterialDataSaved(); } + if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { + EventBridge.emitWebEvent(JSON.stringify({ type: 'materialTargetRequest', entityID: selectedEntityProperties.id })); + } + let activeElement = document.activeElement; if (doSelectElement && typeof activeElement.select !== "undefined") { activeElement.select(); @@ -3629,6 +3779,8 @@ function loaded() { } } } + } else if (data.type === 'materialTargetReply') { + setMaterialTargetData(data.materialTargetData); } }); @@ -3698,26 +3850,6 @@ function loaded() { elDiv.insertBefore(elMaterialDataEditor, elMaterialData); elDiv.insertBefore(elMaterialDataEditorStatus, elMaterialData); - // Special Property Callbacks - let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); - let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); - let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); - elParentMaterialNameString.addEventListener('change', function () { - updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + this.value, false); - }); - elParentMaterialNameNumber.addEventListener('change', function () { - updateProperty("parentMaterialName", this.value, false); - }); - elParentMaterialNameCheckbox.addEventListener('change', function () { - if (this.checked) { - updateProperty("parentMaterialName", elParentMaterialNameNumber.value, false); - showParentMaterialNameBox(true, elParentMaterialNameNumber, elParentMaterialNameString); - } else { - updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + elParentMaterialNameString.value, false); - showParentMaterialNameBox(false, elParentMaterialNameNumber, elParentMaterialNameString); - } - }); - // Collapsible sections let elCollapsible = document.getElementsByClassName("collapse-icon");