From 6f5786e01717f9e862627d66540788d93f17d0b5 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 5 Apr 2019 12:25:25 -0700 Subject: [PATCH 01/19] Added 'metaverse' to dev-build URL. --- tools/nitpick/src/TestRunner.h | 2 +- tools/nitpick/src/common.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/nitpick/src/TestRunner.h b/tools/nitpick/src/TestRunner.h index 6d36f246f7..13549e6eec 100644 --- a/tools/nitpick/src/TestRunner.h +++ b/tools/nitpick/src/TestRunner.h @@ -61,7 +61,7 @@ protected: QString _workingFolder; - const QString DEV_BUILD_XML_URL{ "https://highfidelity.com/dev-builds.xml" }; + const QString DEV_BUILD_XML_URL{ "https://metaverse.highfidelity.com/dev-builds.xml" }; const QString DEV_BUILD_XML_FILENAME{ "dev-builds.xml" }; bool buildXMLDownloaded; diff --git a/tools/nitpick/src/common.h b/tools/nitpick/src/common.h index dc1ca17d08..b375f1ee2b 100644 --- a/tools/nitpick/src/common.h +++ b/tools/nitpick/src/common.h @@ -60,5 +60,5 @@ const double R_Y = 0.212655f; const double G_Y = 0.715158f; const double B_Y = 0.072187f; -const QString nitpickVersion{ "v3.2.0" }; +const QString nitpickVersion{ "v3.2.1" }; #endif // hifi_common_h \ No newline at end of file From 75e1437274fdc557008ee1fe19abbcb3a636a3e9 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 5 Apr 2019 14:55:23 -0700 Subject: [PATCH 02/19] Use optional environment variable for timestamp server URL --- CMakeLists.txt | 5 +++++ cmake/macros/OptionalWinExecutableSigning.cmake | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ba5e1264f..0fad5c10f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,11 @@ else() set(MOBILE 0) endif() +# Use default time server if none defined in environment +if (NOT DEFINED TIMESERVER_URL) + set(TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp") +endif() + set(HIFI_USE_OPTIMIZED_IK OFF) set(BUILD_CLIENT_OPTION ON) set(BUILD_SERVER_OPTION ON) diff --git a/cmake/macros/OptionalWinExecutableSigning.cmake b/cmake/macros/OptionalWinExecutableSigning.cmake index 069fc12fc5..cbefdaea8f 100644 --- a/cmake/macros/OptionalWinExecutableSigning.cmake +++ b/cmake/macros/OptionalWinExecutableSigning.cmake @@ -22,7 +22,7 @@ macro(optional_win_executable_signing) # setup a post build command to sign the executable add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND ${SIGNTOOL_EXECUTABLE} sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 ${EXECUTABLE_PATH} + COMMAND ${SIGNTOOL_EXECUTABLE} sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr ${TIMESERVER_URL} /td SHA256 ${EXECUTABLE_PATH} ) else () message(FATAL_ERROR "HF_PFX_PASSPHRASE must be set for executables to be signed.") From 53ea50d1f345f68d860106dd19b12347e19fb20b Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 5 Apr 2019 15:45:11 -0700 Subject: [PATCH 03/19] Use set_from_env macro. --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fad5c10f1..551cfd2636 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,9 +53,7 @@ else() endif() # Use default time server if none defined in environment -if (NOT DEFINED TIMESERVER_URL) - set(TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp") -endif() +set_from_env(TIMESERVER_URL TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp") set(HIFI_USE_OPTIMIZED_IK OFF) set(BUILD_CLIENT_OPTION ON) From aef4f28194b3e397373805dd4dd879f46e52853a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 3 Apr 2019 00:22:21 +0200 Subject: [PATCH 04/19] unused variables / missing semi-colon --- scripts/system/html/js/entityProperties.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 78a6e26ff0..a3e1bf18e1 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1528,7 +1528,6 @@ const GROUPS_PER_TYPE = { }; const EDITOR_TIMEOUT_DURATION = 1500; -const IMAGE_DEBOUNCE_TIMEOUT = 250; const DEBOUNCE_TIMEOUT = 125; const COLOR_MIN = 0; @@ -2069,7 +2068,7 @@ function createNumberProperty(property, elProperty) { type="number" ${propertyData.placeholder ? 'placeholder="' + propertyData.placeholder + '"' : ''} ${propertyData.readOnly ? 'readonly' : ''}> - `) + `); if (propertyData.min !== undefined) { elInput.setAttribute("min", propertyData.min); @@ -3312,8 +3311,6 @@ function materialTargetPropertyUpdate(propertyValue) { function loaded() { openEventBridge(function() { let elPropertiesList = document.getElementById("properties-list"); - - let templatePropertyRow = document.getElementById('property-row'); GROUPS.forEach(function(group) { let elGroup; From 71770e3f4e9b6a0fbf29a5c01a4a9d32ccf9953c Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 3 Apr 2019 00:32:05 +0200 Subject: [PATCH 05/19] move entity selection update code to its own function --- scripts/system/html/js/entityProperties.js | 637 +++++++++++---------- 1 file changed, 320 insertions(+), 317 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index a3e1bf18e1..f060e0a866 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3308,6 +3308,325 @@ function materialTargetPropertyUpdate(propertyValue) { } +function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { + if (selections.length === 0) { + if (lastEntityID !== null) { + if (editor !== null) { + saveUserData(); + deleteJSONEditor(); + } + if (materialEditor !== null) { + saveMaterialData(); + deleteJSONMaterialEditor(); + } + } + + resetProperties(); + showGroupsForType("None"); + + let elIcon = properties.type.elSpan; + elIcon.innerText = NO_SELECTION; + elIcon.style.display = 'inline-block'; + + deleteJSONEditor(); + getPropertyInputElement("userData").value = ""; + showUserDataTextArea(); + showSaveUserDataButton(); + showNewJSONEditorButton(); + + deleteJSONMaterialEditor(); + getPropertyInputElement("materialData").value = ""; + showMaterialDataTextArea(); + showSaveMaterialDataButton(); + showNewJSONMaterialEditorButton(); + + disableProperties(); + } else if (selections.length > 1) { + deleteJSONEditor(); + deleteJSONMaterialEditor(); + + let ids = []; + let types = {}; + let numTypes = 0; + + for (let i = 0; i < selections.length; ++i) { + ids.push(selections[i].id); + let currentSelectedType = selections[i].properties.type; + if (types[currentSelectedType] === undefined) { + types[currentSelectedType] = 0; + numTypes += 1; + } + types[currentSelectedType]++; + } + + let type = "Multiple"; + if (numTypes === 1) { + type = selections[0].properties.type; + } + + resetProperties(); + showGroupsForType(type); + + let typeProperty = properties["type"]; + typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; + typeProperty.elSpan.style.display = "inline-block"; + + disableProperties(); + } else { + selectedEntityProperties = selections[0].properties; + + if (lastEntityID !== selectedEntityProperties.id && lastEntityID !== null) { + if (editor !== null) { + saveUserData(); + } + if (materialEditor !== null) { + saveMaterialData(); + } + } + + let hasSelectedEntityChanged = lastEntityID !== selectedEntityProperties.id; + + if (!isPropertiesToolUpdate && !hasSelectedEntityChanged && document.hasFocus()) { + // in case the selection has not changed and we still have focus on the properties page, + // we will ignore the event. + return; + } + + let doSelectElement = !hasSelectedEntityChanged; + + // the event bridge and json parsing handle our avatar id string differently. + lastEntityID = selectedEntityProperties.id; + + showGroupsForType(selectedEntityProperties.type); + + if (selectedEntityProperties.locked) { + disableProperties(); + getPropertyInputElement("locked").removeAttribute('disabled'); + } else { + enableProperties(); + disableSaveUserDataButton(); + disableSaveMaterialDataButton() + } + + for (let propertyID in properties) { + let property = properties[propertyID]; + let propertyData = property.data; + let propertyName = property.name; + let propertyValue = getPropertyValue(propertyName); + + let isSubProperty = propertyData.subPropertyOf !== undefined; + if (propertyValue === undefined && !isSubProperty) { + continue; + } + + if (propertyData.hideIfCertified) { + let shouldHide = selectedEntityProperties.certificateID !== ""; + if (shouldHide) { + propertyValue = "** Certified **"; + property.elInput.disabled = true; + } + } + + let isPropertyNotNumber = false; + switch (propertyData.type) { + case 'number': + case 'number-draggable': + isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; + break; + case 'rect': + case 'vec3': + case 'vec2': + isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; + break; + case 'color': + isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; + break; + } + if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { + propertyValue = getPropertyValue(propertyData.fallbackProperty); + } + + switch (propertyData.type) { + case 'string': { + property.elInput.value = propertyValue; + break; + } + case 'bool': { + let inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; + if (isSubProperty) { + let propertyValue = selectedEntityProperties[propertyData.subPropertyOf]; + let subProperties = propertyValue.split(","); + let subPropertyValue = subProperties.indexOf(propertyName) > -1; + property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; + } else { + property.elInput.checked = inverse ? !propertyValue : propertyValue; + } + break; + } + case 'number': { + property.elInput.value = propertyValue; + break; + } + case 'number-draggable': { + let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + let value = propertyValue / multiplier; + if (propertyData.round !== undefined) { + value = Math.round(value.round) / propertyData.round; + } + property.elNumber.setValue(value); + break; + } + case 'rect': + property.elNumberX.setValue(propertyValue.x); + property.elNumberY.setValue(propertyValue.y); + property.elNumberWidth.setValue(propertyValue.width); + property.elNumberHeight.setValue(propertyValue.height); + break; + case 'vec3': + case 'vec2': { + let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + let valueX = propertyValue.x / multiplier; + let valueY = propertyValue.y / multiplier; + let valueZ = propertyValue.z / multiplier; + if (propertyData.round !== undefined) { + valueX = Math.round(valueX * propertyData.round) / propertyData.round; + valueY = Math.round(valueY * propertyData.round) / propertyData.round; + valueZ = Math.round(valueZ * propertyData.round) / propertyData.round; + } + if (propertyData.decimals !== undefined) { + property.elNumberX.setValue(valueX.toFixed(propertyData.decimals)); + property.elNumberY.setValue(valueY.toFixed(propertyData.decimals)); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(valueZ.toFixed(propertyData.decimals)); + } + } else { + property.elNumberX.setValue(valueX); + property.elNumberY.setValue(valueY); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(valueZ); + } + } + break; + } + case 'color': { + property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + + propertyValue.green + "," + + propertyValue.blue + ")"; + if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') { + // Set the color picker inactive before setting the color, + // otherwise an update will be sent directly after setting it here. + $(property.elColorPicker).attr('active', 'false'); + colorPickers['#' + property.elementID].colpickSetColor({ + "r": propertyValue.red, + "g": propertyValue.green, + "b": propertyValue.blue + }); + $(property.elColorPicker).attr('active', 'true'); + } + + property.elNumberR.setValue(propertyValue.red); + property.elNumberG.setValue(propertyValue.green); + property.elNumberB.setValue(propertyValue.blue); + break; + } + case 'dropdown': { + property.elInput.value = propertyValue; + setDropdownText(property.elInput); + break; + } + case 'textarea': { + property.elInput.value = propertyValue; + setTextareaScrolling(property.elInput); + break; + } + case 'icon': { + property.elSpan.innerHTML = propertyData.icons[propertyValue]; + property.elSpan.style.display = "inline-block"; + break; + } + case 'texture': { + property.elInput.value = propertyValue; + property.elInput.imageLoad(property.elInput.value); + break; + } + case 'dynamic-multiselect': { + if (property.data.propertyUpdate) { + property.data.propertyUpdate(propertyValue); + } + break; + } + } + + let showPropertyRules = property.showPropertyRules; + if (showPropertyRules !== undefined) { + for (let propertyToShow in showPropertyRules) { + let showIfThisPropertyValue = showPropertyRules[propertyToShow]; + let show = String(propertyValue) === String(showIfThisPropertyValue); + showPropertyElement(propertyToShow, show); + } + } + } + + updateVisibleSpaceModeProperties(); + + + let json = null; + try { + json = JSON.parse(selectedEntityProperties.userData); + } catch (e) { + // normal text + deleteJSONEditor(); + getPropertyInputElement("userData").value = selectedEntityProperties.userData; + showUserDataTextArea(); + showNewJSONEditorButton(); + hideSaveUserDataButton(); + hideUserDataSaved(); + } + if (json !== null) { + if (editor === null) { + createJSONEditor(); + } + setEditorJSON(json); + showSaveUserDataButton(); + hideUserDataTextArea(); + hideNewJSONEditorButton(); + hideUserDataSaved(); + } + + let materialJson = null; + try { + materialJson = JSON.parse(selectedEntityProperties.materialData); + } catch (e) { + // normal text + deleteJSONMaterialEditor(); + getPropertyInputElement("materialData").value = selectedEntityProperties.materialData; + showMaterialDataTextArea(); + showNewJSONMaterialEditorButton(); + hideSaveMaterialDataButton(); + hideMaterialDataSaved(); + } + if (materialJson !== null) { + if (materialEditor === null) { + createJSONMaterialEditor(); + } + setMaterialEditorJSON(materialJson); + showSaveMaterialDataButton(); + hideMaterialDataTextArea(); + hideNewJSONMaterialEditorButton(); + hideMaterialDataSaved(); + } + + if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { + requestMaterialTarget(); + } + + let activeElement = document.activeElement; + if (doSelectElement && typeof activeElement.select !== "undefined") { + activeElement.select(); + } + } +} + function loaded() { openEventBridge(function() { let elPropertiesList = document.getElementById("properties-list"); @@ -3489,323 +3808,7 @@ function loaded() { if (data.spaceMode !== undefined) { currentSpaceMode = data.spaceMode === "local" ? PROPERTY_SPACE_MODE.LOCAL : PROPERTY_SPACE_MODE.WORLD; } - if (data.selections.length === 0) { - if (lastEntityID !== null) { - if (editor !== null) { - saveUserData(); - deleteJSONEditor(); - } - if (materialEditor !== null) { - saveMaterialData(); - deleteJSONMaterialEditor(); - } - } - - resetProperties(); - showGroupsForType("None"); - - let elIcon = properties.type.elSpan; - elIcon.innerText = NO_SELECTION; - elIcon.style.display = 'inline-block'; - - deleteJSONEditor(); - getPropertyInputElement("userData").value = ""; - showUserDataTextArea(); - showSaveUserDataButton(); - showNewJSONEditorButton(); - - deleteJSONMaterialEditor(); - getPropertyInputElement("materialData").value = ""; - showMaterialDataTextArea(); - showSaveMaterialDataButton(); - showNewJSONMaterialEditorButton(); - - disableProperties(); - } else if (data.selections.length > 1) { - deleteJSONEditor(); - deleteJSONMaterialEditor(); - - let selections = data.selections; - - let ids = []; - let types = {}; - let numTypes = 0; - - for (let i = 0; i < selections.length; ++i) { - ids.push(selections[i].id); - let currentSelectedType = selections[i].properties.type; - if (types[currentSelectedType] === undefined) { - types[currentSelectedType] = 0; - numTypes += 1; - } - types[currentSelectedType]++; - } - - let type = "Multiple"; - if (numTypes === 1) { - type = selections[0].properties.type; - } - - resetProperties(); - showGroupsForType(type); - - let typeProperty = properties["type"]; - typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; - typeProperty.elSpan.style.display = "inline-block"; - - disableProperties(); - } else { - selectedEntityProperties = data.selections[0].properties; - - if (lastEntityID !== '"' + selectedEntityProperties.id + '"' && lastEntityID !== null) { - if (editor !== null) { - saveUserData(); - } - if (materialEditor !== null) { - saveMaterialData(); - } - } - - let hasSelectedEntityChanged = lastEntityID !== '"' + selectedEntityProperties.id + '"'; - - if (!data.isPropertiesToolUpdate && !hasSelectedEntityChanged && document.hasFocus()) { - // in case the selection has not changed and we still have focus on the properties page, - // we will ignore the event. - return; - } - - let doSelectElement = !hasSelectedEntityChanged; - - // the event bridge and json parsing handle our avatar id string differently. - lastEntityID = '"' + selectedEntityProperties.id + '"'; - - showGroupsForType(selectedEntityProperties.type); - - if (selectedEntityProperties.locked) { - disableProperties(); - getPropertyInputElement("locked").removeAttribute('disabled'); - } else { - enableProperties(); - disableSaveUserDataButton(); - disableSaveMaterialDataButton() - } - - for (let propertyID in properties) { - let property = properties[propertyID]; - let propertyData = property.data; - let propertyName = property.name; - let propertyValue = getPropertyValue(propertyName); - - let isSubProperty = propertyData.subPropertyOf !== undefined; - if (propertyValue === undefined && !isSubProperty) { - continue; - } - - if (propertyData.hideIfCertified) { - let shouldHide = selectedEntityProperties.certificateID !== ""; - if (shouldHide) { - propertyValue = "** Certified **"; - property.elInput.disabled = true; - } - } - - let isPropertyNotNumber = false; - switch (propertyData.type) { - case 'number': - case 'number-draggable': - isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; - break; - case 'rect': - case 'vec3': - case 'vec2': - isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; - break; - case 'color': - isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; - break; - } - if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { - propertyValue = getPropertyValue(propertyData.fallbackProperty); - } - - switch (propertyData.type) { - case 'string': { - property.elInput.value = propertyValue; - break; - } - case 'bool': { - let inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; - if (isSubProperty) { - let propertyValue = selectedEntityProperties[propertyData.subPropertyOf]; - let subProperties = propertyValue.split(","); - let subPropertyValue = subProperties.indexOf(propertyName) > -1; - property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; - } else { - property.elInput.checked = inverse ? !propertyValue : propertyValue; - } - break; - } - case 'number': { - property.elInput.value = propertyValue; - break; - } - case 'number-draggable': { - let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let value = propertyValue / multiplier; - if (propertyData.round !== undefined) { - value = Math.round(value.round) / propertyData.round; - } - property.elNumber.setValue(value); - break; - } - case 'rect': - property.elNumberX.setValue(propertyValue.x); - property.elNumberY.setValue(propertyValue.y); - property.elNumberWidth.setValue(propertyValue.width); - property.elNumberHeight.setValue(propertyValue.height); - break; - case 'vec3': - case 'vec2': { - let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let valueX = propertyValue.x / multiplier; - let valueY = propertyValue.y / multiplier; - let valueZ = propertyValue.z / multiplier; - if (propertyData.round !== undefined) { - valueX = Math.round(valueX * propertyData.round) / propertyData.round; - valueY = Math.round(valueY * propertyData.round) / propertyData.round; - valueZ = Math.round(valueZ * propertyData.round) / propertyData.round; - } - if (propertyData.decimals !== undefined) { - property.elNumberX.setValue(valueX.toFixed(propertyData.decimals)); - property.elNumberY.setValue(valueY.toFixed(propertyData.decimals)); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(valueZ.toFixed(propertyData.decimals)); - } - } else { - property.elNumberX.setValue(valueX); - property.elNumberY.setValue(valueY); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(valueZ); - } - } - break; - } - case 'color': { - property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + - propertyValue.green + "," + - propertyValue.blue + ")"; - if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') { - // Set the color picker inactive before setting the color, - // otherwise an update will be sent directly after setting it here. - $(property.elColorPicker).attr('active', 'false'); - colorPickers['#' + property.elementID].colpickSetColor({ - "r": propertyValue.red, - "g": propertyValue.green, - "b": propertyValue.blue - }); - $(property.elColorPicker).attr('active', 'true'); - } - - property.elNumberR.setValue(propertyValue.red); - property.elNumberG.setValue(propertyValue.green); - property.elNumberB.setValue(propertyValue.blue); - break; - } - case 'dropdown': { - property.elInput.value = propertyValue; - setDropdownText(property.elInput); - break; - } - case 'textarea': { - property.elInput.value = propertyValue; - setTextareaScrolling(property.elInput); - break; - } - case 'icon': { - property.elSpan.innerHTML = propertyData.icons[propertyValue]; - property.elSpan.style.display = "inline-block"; - break; - } - case 'texture': { - property.elInput.value = propertyValue; - property.elInput.imageLoad(property.elInput.value); - break; - } - case 'dynamic-multiselect': { - if (property.data.propertyUpdate) { - property.data.propertyUpdate(propertyValue); - } - break; - } - } - - let showPropertyRules = property.showPropertyRules; - if (showPropertyRules !== undefined) { - for (let propertyToShow in showPropertyRules) { - let showIfThisPropertyValue = showPropertyRules[propertyToShow]; - let show = String(propertyValue) === String(showIfThisPropertyValue); - showPropertyElement(propertyToShow, show); - } - } - } - - updateVisibleSpaceModeProperties(); - - let json = null; - try { - json = JSON.parse(selectedEntityProperties.userData); - } catch (e) { - // normal text - deleteJSONEditor(); - getPropertyInputElement("userData").value = selectedEntityProperties.userData; - showUserDataTextArea(); - showNewJSONEditorButton(); - hideSaveUserDataButton(); - hideUserDataSaved(); - } - if (json !== null) { - if (editor === null) { - createJSONEditor(); - } - setEditorJSON(json); - showSaveUserDataButton(); - hideUserDataTextArea(); - hideNewJSONEditorButton(); - hideUserDataSaved(); - } - - let materialJson = null; - try { - materialJson = JSON.parse(selectedEntityProperties.materialData); - } catch (e) { - // normal text - deleteJSONMaterialEditor(); - getPropertyInputElement("materialData").value = selectedEntityProperties.materialData; - showMaterialDataTextArea(); - showNewJSONMaterialEditorButton(); - hideSaveMaterialDataButton(); - hideMaterialDataSaved(); - } - if (materialJson !== null) { - if (materialEditor === null) { - createJSONMaterialEditor(); - } - setMaterialEditorJSON(materialJson); - showSaveMaterialDataButton(); - hideMaterialDataTextArea(); - hideNewJSONMaterialEditorButton(); - hideMaterialDataSaved(); - } - - if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { - requestMaterialTarget(); - } - - let activeElement = document.activeElement; - if (doSelectElement && typeof activeElement.select !== "undefined") { - activeElement.select(); - } - } + handleEntitySelectionUpdate(data.selections, data.isPropertiesToolUpdate); } else if (data.type === 'tooltipsReply') { createAppTooltip.setIsEnabled(!data.hmdActive); createAppTooltip.setTooltipData(data.tooltips); From 8e97c4f3f208c6c062eb6cd20c6fd27d62dd3082 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 8 Apr 2019 15:52:37 +0200 Subject: [PATCH 06/19] multi-editing support for properties --- scripts/system/edit.js | 87 ++- scripts/system/html/css/edit-style.css | 59 +- scripts/system/html/js/draggableNumber.js | 74 +- scripts/system/html/js/entityProperties.js | 760 +++++++++++++-------- 4 files changed, 631 insertions(+), 349 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 0346e1c7a1..dbb429e86a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2361,41 +2361,64 @@ var PropertiesTool = function (opts) { } var i, properties, dY, diff, newPosition; if (data.type === "update") { - if (selectionManager.selections.length > 1) { - for (i = 0; i < selectionManager.selections.length; i++) { - Entities.editEntity(selectionManager.selections[i], data.properties); + + if (data.properties || data.propertiesMap) { + var propertiesMap = data.propertiesMap; + if (propertiesMap === undefined) { + var updateEntityIDs = data.ids !== undefined ? data.ids : selectionManager.selections; + propertiesMap = []; + propertiesMap.push({ + entityIDs: updateEntityIDs, + properties: data.properties + }); } - } else if (data.properties) { - if (data.properties.dynamic === false) { - // this object is leaving dynamic, so we zero its velocities - data.properties.localVelocity = Vec3.ZERO; - data.properties.localAngularVelocity = Vec3.ZERO; - } - if (data.properties.rotation !== undefined) { - data.properties.rotation = Quat.fromVec3Degrees(data.properties.rotation); - } - if (data.properties.localRotation !== undefined) { - data.properties.localRotation = Quat.fromVec3Degrees(data.properties.localRotation); - } - if (data.properties.emitOrientation !== undefined) { - data.properties.emitOrientation = Quat.fromVec3Degrees(data.properties.emitOrientation); - } - if (data.properties.keyLight !== undefined && data.properties.keyLight.direction !== undefined) { - var currentKeyLightDirection = Vec3.toPolar(Entities.getEntityProperties(selectionManager.selections[0], ['keyLight.direction']).keyLight.direction); - if (data.properties.keyLight.direction.x === undefined) { - data.properties.keyLight.direction.x = currentKeyLightDirection.x; + + var sendListUpdate = false; + propertiesMap.forEach(function(propertiesObject) { + var properties = propertiesObject.properties; + var updateEntityIDs = propertiesObject.entityIDs; + if (properties.dynamic === false) { + // this object is leaving dynamic, so we zero its velocities + properties.localVelocity = Vec3.ZERO; + properties.localAngularVelocity = Vec3.ZERO; } - if (data.properties.keyLight.direction.y === undefined) { - data.properties.keyLight.direction.y = currentKeyLightDirection.y; + if (properties.rotation !== undefined) { + properties.rotation = Quat.fromVec3Degrees(properties.rotation); } - data.properties.keyLight.direction = Vec3.fromPolar(data.properties.keyLight.direction.x, data.properties.keyLight.direction.y); - } - Entities.editEntity(selectionManager.selections[0], data.properties); - if (data.properties.name !== undefined || data.properties.modelURL !== undefined || data.properties.materialURL !== undefined || - data.properties.visible !== undefined || data.properties.locked !== undefined) { + if (properties.localRotation !== undefined) { + properties.localRotation = Quat.fromVec3Degrees(properties.localRotation); + } + if (properties.emitOrientation !== undefined) { + properties.emitOrientation = Quat.fromVec3Degrees(properties.emitOrientation); + } + if (properties.keyLight !== undefined && properties.keyLight.direction !== undefined) { + var currentKeyLightDirection = Vec3.toPolar(Entities.getEntityProperties(selectionManager.selections[0], ['keyLight.direction']).keyLight.direction); + if (properties.keyLight.direction.x === undefined) { + properties.keyLight.direction.x = currentKeyLightDirection.x; + } + if (properties.keyLight.direction.y === undefined) { + properties.keyLight.direction.y = currentKeyLightDirection.y; + } + properties.keyLight.direction = Vec3.fromPolar(properties.keyLight.direction.x, properties.keyLight.direction.y); + } + + updateEntityIDs.forEach(function (entityID) { + Entities.editEntity(entityID, properties); + }); + + if (properties.name !== undefined || properties.modelURL !== undefined || properties.materialURL !== undefined || + properties.visible !== undefined || properties.locked !== undefined) { + + sendListUpdate = true; + } + + }); + if (sendListUpdate) { entityListTool.sendUpdate(); } } + + if (data.onlyUpdateEntities) { blockPropertyUpdates = true; } else { @@ -2405,9 +2428,9 @@ var PropertiesTool = function (opts) { selectionManager._update(false, this); blockPropertyUpdates = false; } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') { - //the event bridge and json parsing handle our avatar id string differently. - var actualID = data.id.split('"')[1]; - Entities.editEntity(actualID, data.properties); + data.ids.forEach(function(entityID) { + Entities.editEntity(entityID, data.properties); + }); } else if (data.type === "showMarketplace") { showMarketplace(); } else if (data.type === "action") { diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index d6a281b0c4..6190469c98 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -193,8 +193,8 @@ td { } td.hidden { - padding-left: 0px; - padding-right: 0px; + padding-left: 0; + padding-right: 0; } td.url { @@ -262,6 +262,40 @@ input[type="text"] { width: 100%; } +input.multi-diff:not(:focus) + span.multi-diff, +.draggable-number.multi-diff span.multi-diff, +dl>dt.multi-diff:not(:focus) + span.multi-diff { + visibility: visible; + position: absolute; + display: inline-block; + z-index: 2; + left: 20px; + width: 50px; + height: 40%; + background-image: linear-gradient(transparent 0%, transparent 10%, #afafaf 10%, #afafaf 20%, transparent 20%, transparent 45%, #afafaf 45%, #afafaf 55%, transparent 55%, transparent 80%, #afafaf 80%, #afafaf 90%, transparent 90%, transparent 100%); + background-repeat: no-repeat; + pointer-events: none; +} + +input.multi-diff:not(:focus)::-webkit-input-placeholder, input.multi-diff:not(:focus) { + color: transparent; +} + +.draggable-number.multi-diff .text { + color: transparent; +} + +.dropdown > span.multi-diff { + top: 5px; + left: 10px; +} + +.text, .url { + position: relative; + display: flex; + align-items: center; +} + input[type="search"] { height: 28px; width: 100%; @@ -333,7 +367,7 @@ input[type=range]::-webkit-slider-thumb { input[type=range]::-webkit-slider-thumb:hover { background-color: white; } -input[type=range]:focus { /*#252525*/ +input[type=range]:focus { outline: none; } @@ -443,6 +477,12 @@ input[type=checkbox]:checked + label { input[type=checkbox]:checked + label:hover { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEySURBVDhPnZLPSsNAEMa/XVPBCE0RhNy0OarP4Av4AD6JB0GwVBA8efBBxHsgh4CQswcRoUIpiIpVAm3zZ5M4szFSbQPBH3xkJvNNZskOer2eLIriKM/ze1JOcS1UHmdZduF5ngEKjr/fN4Z6+oKerwA2gxC4HAFPEWVLsAzgZAvYt3Q6Enw6jg7uBAaTFMNwhpnKdbXCkAJdy8ROu4XrXW2HTJIErHcFDD6nC02Mom8PwymeE2gvS0ZRBBaTlsOXEmdlrfLLOI7Bakrl/zWxCT8T/904f9QW/b06qtrCUdtFCqdjYs2Q2jAPX8c2XQd7Kr/wfV8vwIPs4Ga1ixe5Xrr/YFLTYfKIvWzM6ZtwXZdX7lxXG0L+sxXHcW5t254opRzawQ0S72+dPmjTroIgOP0CQSMt5LDn1T8AAAAASUVORK5CYII=); } +input.multi-diff[type=checkbox] + label { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAPcXpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjarZpplts6DoX/cxW9BHEml8MJ5/QOevn9gZTtmlKVlxc78SBLJIgLXFxQZdb//ivmPzxCLsGEmEuqKV08Qg3VNT6U6zzOu73Cfj2Pdb/b98fN8wfHIc+7P1/Tfdw2jsfXBTncx/v74yaPe5xyD/SY+R7Q68yOD/d55R7Iu3Pc3t9Nva9r4c1y7v9j7SEuew/68XvIOGNGDnpn3PIc5zXpLB4LfPWN93BenR4pfI77lcfXvjPPjx+c9/z0wXdXu4/7964wV7pPSB98dB+38WvfbQ+9tci+Zn73Q232ZdsH34nMIrLO6lpIeCqZe1GPpexPnNhx5fFG4pn5H/mc97PyLCxxgNgEzc5zGFutw9tig522WbFrvw87MDG45TLvzg3n97His6tubFCCPq24DDzTgIXzA9Q8h93TFrvnrXu+YQszT8uZzjKY5YpPT/PVwT95PgcS0dDFweXpK+xyGoCYocjpK2cBiJXbp3H7dz/N9R6bB7AeBON2c2GB7epniB7tK7b8xtlzXryCuU5q2DzvAXARc0eMsR4ErmR9tMle2blsLX4s4NOw3BH2HQRsjG5aI2DjfQKc4nRursl2n+uiO4ehFoCIPvkMNCQQYIUQiZ8cCjHUoo/BxBhTzLHEGlvyKaSYUspJOapln0OOOeWcS665FV9CiSWVXEqppVVXPRQWa6rZ1FJrbY1JG0M3rm6c0Vp33ffQY08999Jrb4PwGWHEkUYeZdTRppt+kv4zzWxmmXW2ZRehtMKKK628yqqrCbEmXoJESZKlSJX2RO1G9T1q9gNy36Nmb9QUsbDPyy/UOJzzYwirdBIVMxBzwYJ4VgQIaKeYXcWG4BQ5xeyqjqSIDtRsVHCmVcRAMCzrotgndi/kvsXNxPCPcHO/Qs4odH8DOaPQ3ch9xu0L1GbbFcVvgDQL1aeXF6OFqPEPbvV5LjUpSZ8sXPk5tmBD52MkzqeLWn46v+1DNkT7fDcfD3zz/hpL/VfhKIAid6rIrmth6AdRjphXiVFas20fG8WlLFLg3lUCJVQB2wu4fJ9z5JnKfq2uGNYUV9c591x//G5+OCEdA7SG1urhjDkCMRda79X2BnCWJbNyyF8XMUtvLGJI12+gN2X5kCW5tX8Xv0Tfi3Twmyyf0a97mTbxZhQvQtKNzJyyQtcYclY6MVAhmoTfXGprrNQ6kWthDWvduMagHFlbobvoReFPaUyZq+iM3ssCYJulrSCSS0yNwI7XIO5qS7MSZ95biU21FIEcZdbus5jcvIyNUYl+pOp9tXVCIQvAhuuhtRylN499M9pBNCTSPQwVHzaSgUJWTjGzkp4ypXQ55uCYEEeWvNyeAaMYIGZmluBZUBdSBo4nI0ZyaoJeYF5XqE0M+WHg2Elr+xjFfzmIjmH+xiA6hvkbg+wU+RuD6Bjm3w/igIe6tuMasnkMkzIpCxv50Vep4L2yDeSx9DKKrNF2pPdnfJArsffWjaYKEmi5nld3KelpEc0jnYnrzCgkf9Wwagtopbyu7m0jHsMMa0AVzkJe0Vszr62qYmwQMpcvjTy+ukqqEIeT0CYzHJlBfmo2x2JnoCaUoTUfoqqBD+YNdWkGtbFWcE0zaMJNRVyKInVtSyWP2uJ0d2LHLnNoXsTUIylCQg3fyB2pZI1mo7ddq4+gCldJ2AcnuogVPmt9XZPMCMuj4igxq6AjfTOsp9WFpZ1EbmV6Ck3kulRUwg5eASKVQ6msvc2syTxzIDGbL6w8pZmGSR2Xku1DYth4LAW0yiX3IT2Q/CTjG5w6ChTVuk+yKKl6TlKlLA0f5an8gx+msragni787CdKOxMN1KopPQS/aWY2/5jOMkbJsnIn0ZWP0GoUPqhOUip+dr9GWnJGB5I9vsYa7P/lgEnBzKYjlRdxjCPzsRbPbHtBRtrT4PdDPo2+JMHPtnez8rjnSC+jz0E/4VAdjFLvtTbbNeYlx+aprrWLFqJs35o+UAH5EgINLlZUbootM4x2UQdbm9UnpFhDCqRYFprT00SMYJMfy7UFyp66xocmaBSmDEsG2q1OBk9QLgwddm55aB3jqicDyMiaCF+UElFLNK6ZwjInCVF5Xda6yMTUB7+qdm6ZYJGcSfQEdc+0CGG81rZYEBqYXB7CQctR2gmD4bVrUVRhlO/BqW5L09z1iMZBKqFUphyfKot4ZZ1iV3HdUML7JM4JW18Heqc0RA0P+CiSndNnpGAJ+Qpa9yKeXGlUAi7JWJ1WJwSxrhnC14cEENv3eGvDblsQxBtugV0yChRRWFeAChr6j3yn7NUwYJ5AJV8tVeNaG8Gnu9q7Rxn++E48RRVz87BElNWJjbl2ZUNLVYP6IpxXiyz4xJFGSBiQUi+hDc1X2muCv787A0wQyTQkDkm6tBcZuLGOsAoEZiOZ1UbdkQjhUBWHhXDQh+vKOhrVFnrvrIp89fu3XOlwi5mt1LVwh+8EkFMiswfAjIdRUk84WTpUXAjuS5kR+UrTeVkULDBSaSdKZmZ/mG7OBJhMN88yVq91w59dnzK26CmWQrF89xuY3DfIy6i0k1z9Yc3Wbeqp5aOFUNaPwwQfnB/gwEH+RELVt1oyGbgQUih0Y0v/FVLfIagliqI1uxKtJZwpkEUdO9eOV4JXHPFAOeyJjFFrSz8uhxchdQrGqPCypzCIEpQgpSAOg88mih4W0UM0HuqwG42O8XJ5KiGKcdeOPlkfugsA8LTmJT+PEJF+5wD2YnYYsJW7VeHzi0eeJ50dDHR26mZpc+jktujkey3mCRhRQx3Ybt5GDFoZApiUgsCgCwIHdu4njal4kUqNahD4FWO7wfiqtJsbJQOisrrS49ZOS/3bQJhPP8zZd0ikl5Ov1jPcuusrDA6DXhsTkhVQQs4pVBMJBpiFRH7jZ6Uwi6bYgQmoeP1BeYTVjsi5hkbZmlm3uRotxO3Ul/+rungo7YUP5qrb4ye3k5y6gUCF8/IhHdIP6bB25EEYemwXMCU2p17ZwZXeZETr07XcqTdk/jx6JTZbf9mEmR+7NFhae/Wsq9rl4Yb+CB3Ap4Z1ypGCn7V/hup6d0EXjLDQ9b7xMqul52/bP4+kICeErpuhr6zyOHe6c14pAOB1Aug4/lFqvvJ5OPwwdwCr3812/GrDTrXA0wnLy+vt9vpbn3/2uPrbmfb0ts7w5/42v9MVH3+jUr7yuJbSteMIl3/2uJ3ppITEtz7H0Bz77Z9w+2f73bxx/O13Dbxfez5RJm31x9b0+mxsQ+pvIRHm28CXNmjsH1a9j/z1ReSbn0L/dyPf/BT6vxv55qfQ/4zDNY+1uqvzIh4jM12p4yR0NK9xQBHe65ZN4owLQGBbZViBE9BwiI+gPtZKjCAu5Xa42fad8lu1MLulVXlvLthWNw7hlQiKg8oq9NG1E7I+YDAbB0haIM99cAdCWYgo5T9pDgEmu/K9BwFUCCz3qNTmTam+cbAxggPElyoaLWjJowzY9P1Oi/nqB2TLpBekiuqdgnmmvm498KoJWybc1VjMsxy3kmbgAlRdxueEVD2K9+F/oiV7IvZknXyoyebroqyP4/O4kLfJvcFAQ2huMaE9aNK8mXGaC/1KJ62a152+Gc07ySLdtVnZf6awm8G2ACfU0KIheyyagkShm1m1FI8QvnR1DyGswh9lomunEG/BTB27+MURv6mlnKv269maTskspdUrCIGJLv8klGfEbQmRQfRlbcpYYehbG6O4nG61bmL7RhOrejmy+NIt3ao3r7S9zNsGNcG5M2kVs4RyTwwjEm2+CjV5icMoqCCzvK73NRytEHmuTeNoBG9FkNHZBJyDQGWFYVazuycckYM6veXj9I13zxGFPQ/cq72Fe+s8pNuaynM9uWq2Bli7tYnLWo2g9mh0vsA5vIrNW6pr3eh2adYbbpXoib7vnf2GjamchIRn+JCnbmSAV6kxLDogriq2lnSh8xvRY5rCIPdmQLWX6vm6/JV0x6Tr5qhufULikAhtfqEF0n2cgMa7iI6Jf2g38zBV2xMk+4K5LFPS17vGxFO3zqXjwOYIHzs8bkX64SYiDhbj06gk3spVs8WIQxCN08DvIxzogZC2rLfS0UTt3evaOwN+7ww47dv9bp+HKtG+NI60Re+zdPoR+neoVlLeo2ojofyjOmyNszNh965DRbFT/em5o1DLmootMWdobS0f7bpuaH0xGPjR5DuS652Zuh+ghhrdEHjZ+mFANTfr1pju4MxUISB5uEH3MfYM54B5OqbsrY5jLjn5CP1H5AueLdk5Fy6G6Hi27664ETM+lGh0f6e1YKEtdzd3qTIhxnONi3nfPNaNfNAdtNHdFQfq1CawjTlSRWjjhqEarX635+8YkRJETHa5I1I3Y2xrFKFFMhJdPHIScnjXLmfEXvzsdVtCqWoN3SCYtNZJGny9k6SP7qGtZ6LstoVqpjvYj/bGlMvlYxHSfbGiBJyhJN1FRjCS2aIdSENZEqONSrt3tkPT/TuqcbuLgilaFSiz1LFEEuage/B2xsISWx1luZP/acZNxPgb4PbFc290x+iLo+8/O90QFv1/9ThFt9GzigH8QeM0u9aOOZjPad4HOFOoewxZT7GcWlB0j62Ri7qHv2AsmNwlFa19id7/gaqODgrFUYdA80RbHqoWmm6mktqcUs3SYc+gCZruSLOMUtj3DQhj/o1V8e7ZiXxcp/H81p5UzLfznWHLl6M+bTmmmHtACu8ZTalsj7dH07+J+H68h5XmKzP/xErzlZl/YqX5XWf+ZKX5ZGYZpzQn+lY67KmE5pUopO44onTRXsdDOTCSRmeZDhqh7rnmq6c4Ue9QXxBBuk5/rndBGAEjwpSFTm5Pa6mOGjG1U9pIZDOWFvARiGqre09lxEll09s5U0jy3lSD0h6gqxrpjSqhwZAA/9JDrNKjHdVZKi2SV5XIch1egkRdVEkzUSSc7HpXiXD2jVhsvPqbXSTdbVfv10xAslhSaRt81XPrq57QX1u4Zr1T5qw/6tuHkejlbbxiZUlIcRaXfcJHSUoU32O/Qtp/hFH8IuNzC1ukoNLSVs17D8uGX34x4ffO+/GL+WcXzZriZj0X0F264blukWFQdGvSJ9071sTEuVl47Thb4xYSus/Q9IYKvfHI7W7ehBKo9wSoSkb0Burl9dbqbEqS1g+3h6V0z6p/vLEhCD7pPk7SvwACTxXz12vLl4EeH/7t+z1Qo2ciECjKUJpuRmu7h2jRtkj/6kU0F0ZE8QyZu0UoWmkp2+b/lXgM2mXckDQAAAAGYktHRABnAJUApY81PV4AAAGaSURBVCjPfZIxaFNRFIa/83qfTSM2VkhwcKhtwcW6SOkSxLU4SEU3BxXcFUEHBbPY1cFFcBQXXdycxaZQRBdTEkEcCoVihGqwic++/MeheZrG2ANnOPf837k/91yrVCpRmqa3zOyau58wM+M/4e5b7v6kWq3eDWma3jGzJYB9GHr9CTO7XS6XkwDcBPhanObj7AI/xktDofhXm5PvX74obTYumtnVYGZFgNrpC3yxHOvNFp1Ue6AQGccL+XpjduFKabNxHjgWSUISSe4Qn7+1/4EAUvn39Vb7UufgxIikA5L4AwLsyIe5TIBzSdfXgJlMH3y4uB9aBKq9ejLTB7n2A68Dr/rqXKYPmc0h8QB4OnD2KdNHLie7Po4MYBk4C9zbs47d3geXu8v/Pk7caTF1OH9jLERngNeD65gs5Ik7rURSVxJBUhMoziw/2wlzi28KxaOjwM9B3yHZtumV55clBaAZvOsPPfKl8Y16fGqj/q5P2xkYkAdGtftnH4+svl1dmZ+b33b3KZwjOPQyxhnry4CzhXhUW6vd/w0H6dKzonFkygAAAABJRU5ErkJggg==) +} +input.multi-diff[type=checkbox] + label:hover { + background-image: url(data:image/png;base64,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) +} .icon-input input { position: relative; @@ -731,8 +771,6 @@ span.indented { .dropdown dl { clear: both; cursor: pointer; -} -.dropdown dl { font-family: FiraSans-SemiBold; font-size: 15px; width: 292px; @@ -741,7 +779,10 @@ span.indented { color: #afafaf; background: #575757; position: relative; + display: flex; + align-items: center; } + .dropdown dl[dropped="true"] { color: #404040; background: linear-gradient(#afafaf, #afafaf); @@ -878,6 +919,8 @@ div.refresh { div.refresh input[type="button"] { float: right; margin-right: -44px; + position: relative; + left: 10px; } .color-picker { @@ -930,6 +973,8 @@ div.refresh input[type="button"] { position: relative; height: 28px; flex: 0 1 124px; + display: flex; + align-items: center; } .draggable-number .text { @@ -1735,3 +1780,7 @@ input[type=number].hide-spinner::-webkit-inner-spin-button { -webkit-appearance: none; visibility: hidden; } + +div.jsoneditor-menu a.jsoneditor-poweredBy { + display: none; +} diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 0d6af01ebd..96280b82f6 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -13,6 +13,7 @@ function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) { this.min = min; this.max = max; this.step = step !== undefined ? step : 1; + this.multiDiffModeEnabled = false; this.decimals = decimals; this.dragStartFunction = dragStart; this.dragEndFunction = dragEnd; @@ -20,6 +21,7 @@ function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) { this.initialMouseEvent = null; this.lastMouseEvent = null; this.valueChangeFunction = null; + this.multiDiffDragFunction = null; this.initialize(); } @@ -71,21 +73,26 @@ DraggableNumber.prototype = { } if (this.dragging && this.lastMouseEvent) { let initialValue = this.elInput.value; - let dx = event.clientX - this.lastMouseEvent.clientX; - let changeValue = dx !== 0; - if (changeValue) { - while (dx !== 0) { - if (dx > 0) { - this.elInput.stepUp(); - --dx; - } else { - this.elInput.stepDown(); - ++dx; + let changeDelta = event.clientX - this.lastMouseEvent.clientX; + if (changeDelta !== 0) { + if (this.multiDiffModeEnabled) { + if (this.multiDiffDragFunction) { + this.multiDiffDragFunction(changeDelta * this.step); + } + } else { + while (changeDelta !== 0) { + if (changeDelta > 0) { + this.elInput.stepUp(); + --changeDelta; + } else { + this.elInput.stepDown(); + ++changeDelta; + } + } + this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); } - } - this.inputChange(); - if (this.valueChangeFunction) { - this.valueChangeFunction(); } } this.lastMouseEvent = event; @@ -124,20 +131,39 @@ DraggableNumber.prototype = { } }, - setValue: function(newValue) { - if (newValue !== "" && this.decimals !== undefined) { - this.elInput.value = parseFloat(newValue).toFixed(this.decimals); - } else { - this.elInput.value = newValue; + setValue: function(newValue, isMultiDiff) { + if (isMultiDiff !== undefined) { + this.setMultiDiff(isMultiDiff); + } + + if (!isMultiDiff) { + if (newValue !== "" && this.decimals !== undefined) { + this.elInput.value = parseFloat(newValue).toFixed(this.decimals); + } else { + this.elInput.value = newValue; + } + this.elText.firstChild.data = this.elInput.value; + } + }, + + setMultiDiff: function(isMultiDiff) { + this.multiDiffModeEnabled = isMultiDiff; + if (isMultiDiff) { + this.elDiv.classList.add('multi-diff'); + } else { + this.elDiv.classList.remove('multi-diff'); } - this.elText.firstChild.data = this.elInput.value; }, setValueChangeFunction: function(valueChangeFunction) { this.valueChangeFunction = valueChangeFunction.bind(this.elInput); this.elInput.addEventListener("change", this.valueChangeFunction); }, - + + setMultiDiffDragFunction: function(multiDiffDragFunction) { + this.multiDiffDragFunction = multiDiffDragFunction; + }, + inputChange: function() { let value = this.elInput.value; if (this.max !== undefined) { @@ -203,7 +229,10 @@ DraggableNumber.prototype = { this.elRightArrow.className = 'right-arrow'; this.elRightArrow.innerHTML = 'D'; this.elRightArrow.addEventListener("click", this.onStepUp); - + + this.elMultiDiff = document.createElement('span'); + this.elMultiDiff.className = 'multi-diff'; + this.elInput = document.createElement('input'); this.elInput.className = "input"; this.elInput.setAttribute("type", "number"); @@ -220,6 +249,7 @@ DraggableNumber.prototype = { this.elDiv.appendChild(this.elLeftArrow); this.elDiv.appendChild(this.elText); this.elDiv.appendChild(this.elInput); + this.elDiv.appendChild(this.elMultiDiff); this.elDiv.appendChild(this.elRightArrow); } }; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index f060e0a866..df244d435a 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html /* global alert, augmentSpinButtons, clearTimeout, console, document, Element, - EventBridge, JSONEditor, openEventBridge, setTimeout, window, _ $ */ + EventBridge, JSONEditor, openEventBridge, setTimeout, window, _, $ */ const DEGREES_TO_RADIANS = Math.PI / 180.0; @@ -20,6 +20,16 @@ const PROPERTY_SPACE_MODE = { WORLD: 2 }; +// Multiple-selection behavior +const PROPERTY_MULTI_DISPLAY_MODE = { + DEFAULT: 0, + /** + * Comma separated values + * Limited for properties with type "string" or "textarea" and readOnly enabled + */ + COMMA_SEPARATED_VALUES: 1, +}; + const GROUPS = [ { id: "base", @@ -45,6 +55,7 @@ const GROUPS = [ placeholder: "ID", readOnly: true, replaceID: "placeholder-property-id", + multiDisplayMode: PROPERTY_MULTI_DISPLAY_MODE.COMMA_SEPARATED_VALUES, }, { label: "Description", @@ -1552,7 +1563,7 @@ const ENABLE_DISABLE_SELECTOR = "input, textarea, span, .dropdown dl, .color-pic const PROPERTY_NAME_DIVISION = { GROUP: 0, PROPERTY: 1, - SUBPROPERTY: 2, + SUB_PROPERTY: 2, }; const RECT_ELEMENTS = { @@ -1587,11 +1598,53 @@ let properties = {}; let propertyRangeRequests = []; let colorPickers = {}; let particlePropertyUpdates = {}; -let selectedEntityProperties; -let lastEntityID = null; +let selectedEntityIDs = new Set(); +let currentSelections = []; let createAppTooltip = new CreateAppTooltip(); let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; + + +// mergeDeep function from https://stackoverflow.com/a/34749873 +/** + * Simple object check. + * @param item + * @returns {boolean} + */ +function isObject(item) { + return (item && typeof item === 'object' && !Array.isArray(item)); +} + +/** + * Deep merge two objects. + * @param target + * @param sources + */ +function mergeDeep(target, ...sources) { + if (!sources.length) { + return target; + } + const source = sources.shift(); + + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (!source.hasOwnProperty(key)) { + continue; + } + if (isObject(source[key])) { + if (!target[key]) { + Object.assign(target, { [key]: {} }); + } + mergeDeep(target[key], source[key]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + + return mergeDeep(target, ...sources); +} + function createElementFromHTML(htmlString) { let elTemplate = document.createElement('template'); elTemplate.innerHTML = htmlString.trim(); @@ -1691,6 +1744,7 @@ function resetProperties() { switch (propertyData.type) { case 'number': case 'string': { + property.elInput.classList.remove('multi-diff'); if (propertyData.defaultValue !== undefined) { property.elInput.value = propertyData.defaultValue; } else { @@ -1699,6 +1753,7 @@ function resetProperties() { break; } case 'bool': { + property.elInput.classList.remove('multi-diff'); property.elInput.checked = false; break; } @@ -1765,7 +1820,11 @@ function resetProperties() { } } } - + + resetServerScriptStatus(); +} + +function resetServerScriptStatus() { let elServerScriptError = document.getElementById("property-serverScripts-error"); let elServerScriptStatus = document.getElementById("property-serverScripts-status"); elServerScriptError.parentElement.style.display = "none"; @@ -1774,43 +1833,71 @@ function resetProperties() { function showGroupsForType(type) { if (type === "Box" || type === "Sphere") { - type = "Shape"; + showGroupsForTypes(["Shape"]); + return; } - - let typeGroups = GROUPS_PER_TYPE[type]; + showGroupsForTypes([type]); +} - for (let groupKey in elGroups) { - let elGroup = elGroups[groupKey]; - if (typeGroups && typeGroups.indexOf(groupKey) > -1) { +function showGroupsForTypes(types) { + Object.entries(elGroups).forEach(([groupKey, elGroup]) => { + if (types.map(type => GROUPS_PER_TYPE[type].includes(groupKey)).every(function (hasGroup) { return hasGroup; })) { elGroup.style.display = "block"; } else { elGroup.style.display = "none"; } - } + }); } -function getPropertyValue(originalPropertyName) { - // if this is a compound property name (i.e. animation.running) +function getMultiplePropertyValue(originalPropertyName) { + // if this is a compound property name (i.e. animation.running) // then split it by . up to 3 times to find property value - let propertyValue; + let propertyValues = []; let splitPropertyName = originalPropertyName.split('.'); if (splitPropertyName.length > 1) { let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; - let groupProperties = selectedEntityProperties[propertyGroupName]; - if (groupProperties === undefined || groupProperties[propertyName] === undefined) { - return undefined; - } - if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUBPROPERTY + 1) { - let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUBPROPERTY]; - propertyValue = groupProperties[propertyName][subPropertyName]; - } else { - propertyValue = groupProperties[propertyName]; - } + propertyValue = currentSelections.map(selection => { + let groupProperties = selection.properties[propertyGroupName]; + if (groupProperties === undefined || groupProperties[propertyName] === undefined) { + return undefined; + } + if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUB_PROPERTY + 1) { + let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUB_PROPERTY]; + return groupProperties[propertyName][subPropertyName]; + } else { + return groupProperties[propertyName]; + } + }); } else { - propertyValue = selectedEntityProperties[originalPropertyName]; + propertyValues = currentSelections.map(selection => selection.properties[originalPropertyName]); } - return propertyValue; + + let detailedValues = []; + propertyValues.forEach(function(propertyValue) { + if (typeof propertyValues === "object") { + + } else { + detailedValues.push(propertyValue); + } + }); + + const uniquePropertyValues = [...new Set(propertyValues)]; + const isMultiDiffValue = uniquePropertyValues.length > 1; + + if (isMultiDiffValue) { + return { + value: undefined, + values: propertyValues, + isMultiDiffValue: true + } + } + + return { + value: uniquePropertyValues[0], + values: propertyValues, + isMultiDiffValue: false + }; } function updateVisibleSpaceModeProperties() { @@ -1828,12 +1915,11 @@ function updateVisibleSpaceModeProperties() { } } - /** * PROPERTY UPDATE FUNCTIONS */ -function updateProperty(originalPropertyName, propertyValue, isParticleProperty) { +function createPropertyUpdateObject(originalPropertyName, propertyValue) { let propertyUpdate = {}; // if this is a compound property name (i.e. animation.running) then split it by . up to 3 times let splitPropertyName = originalPropertyName.split('.'); @@ -1841,8 +1927,8 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; propertyUpdate[propertyGroupName] = {}; - if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUBPROPERTY + 1) { - let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUBPROPERTY]; + if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUB_PROPERTY + 1) { + let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUB_PROPERTY]; propertyUpdate[propertyGroupName][propertyName] = {}; propertyUpdate[propertyGroupName][propertyName][subPropertyName] = propertyValue; } else { @@ -1851,6 +1937,12 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) } else { propertyUpdate[originalPropertyName] = propertyValue; } + return propertyUpdate; +} + +function updateProperty(originalPropertyName, propertyValue, isParticleProperty) { + let propertyUpdate = createPropertyUpdateObject(originalPropertyName, propertyValue); + // queue up particle property changes with the debounced sync to avoid // causing particle emitting to reset excessively with each value change if (isParticleProperty) { @@ -1860,7 +1952,7 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) particleSyncDebounce(); } else { // only update the entity property value itself if in the middle of dragging - // prevent undo command push, saving new property values, and property update + // prevent undo command push, saving new property values, and property update // callback until drag is complete (additional update sent via dragEnd callback) let onlyUpdateEntity = properties[originalPropertyName] && properties[originalPropertyName].dragging === true; updateProperties(propertyUpdate, onlyUpdateEntity); @@ -1877,15 +1969,32 @@ function updateProperties(propertiesToUpdate, onlyUpdateEntity) { onlyUpdateEntity = false; } EventBridge.emitWebEvent(JSON.stringify({ - id: lastEntityID, + ids: [...selectedEntityIDs], type: "update", properties: propertiesToUpdate, onlyUpdateEntities: onlyUpdateEntity })); } +function updateMultiDiffProperties(propertiesMapToUpdate, onlyUpdateEntity) { + if (onlyUpdateEntity === undefined) { + onlyUpdateEntity = false; + } + EventBridge.emitWebEvent(JSON.stringify({ + type: "update", + propertiesMap: propertiesMapToUpdate, + onlyUpdateEntities: onlyUpdateEntity + })); + console.log(JSON.stringify({ + type: "update", + propertiesMap: propertiesMapToUpdate, + onlyUpdateEntities: onlyUpdateEntity + })) +} + function createEmitTextPropertyUpdateFunction(property) { return function() { + property.elInput.classList.remove('multi-diff'); updateProperty(property.name, this.value, property.isParticleProperty); }; } @@ -1905,8 +2014,26 @@ function createDragStartFunction(property) { function createDragEndFunction(property) { return function() { property.dragging = false; - // send an additional update post-dragging to consider whole property change from dragStart to dragEnd to be 1 action - this.valueChangeFunction(); + + if (this.multiDiffModeEnabled) { + let propertyMultiValue = getMultiplePropertyValue(property.name); + let updateObjects = []; + const selectedEntityIDsArray = [...selectedEntityIDs]; + + for (var i = 0; i < selectedEntityIDsArray.length; ++i) { + let entityID = selectedEntityIDsArray[i]; + updateObjects.push({ + entityIDs: [entityID], + properties: createPropertyUpdateObject(property.name, propertyMultiValue.values[i]), + }); + } + + // send a full updateMultiDiff post-dragging to count as an action in the undo stack + updateMultiDiffProperties(updateObjects); + } else { + // send an additional update post-dragging to consider whole property change from dragStart to dragEnd to be 1 action + this.valueChangeFunction(); + } }; } @@ -2004,7 +2131,7 @@ function createStringProperty(property, elProperty) { + ${propertyData.readOnly ? 'readonly' : ''}/> `); @@ -2013,7 +2140,12 @@ function createStringProperty(property, elProperty) { elInput.addEventListener('change', propertyData.onChange); } + + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + elProperty.appendChild(elInput); + elProperty.appendChild(elMultiDiff); if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, false); @@ -2046,7 +2178,10 @@ function createBoolProperty(property, elProperty) { let subPropertyOf = propertyData.subPropertyOf; if (subPropertyOf !== undefined) { elInput.addEventListener('change', function() { - updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], + let subPropertyMultiValue = getMultiplePropertyValue(subPropertyOf); + + updateCheckedSubProperty(subPropertyOf, + subPropertyMultiValue.value, elInput, propertyName, property.isParticleProperty); }); } else { @@ -2067,7 +2202,7 @@ function createNumberProperty(property, elProperty) { class='hide-spinner' type="number" ${propertyData.placeholder ? 'placeholder="' + propertyData.placeholder + '"' : ''} - ${propertyData.readOnly ? 'readonly' : ''}> + ${propertyData.readOnly ? 'readonly' : ''}/> `); if (propertyData.min !== undefined) { @@ -2085,7 +2220,11 @@ function createNumberProperty(property, elProperty) { elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(property)); + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + elProperty.appendChild(elInput); + elProperty.appendChild(elMultiDiff); if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, false); @@ -2124,6 +2263,37 @@ function createNumberDraggableProperty(property, elProperty) { let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); elDraggableNumber.setValueChangeFunction(valueChangeFunction); + + elDraggableNumber.setMultiDiffDragFunction((changedDelta) => { + let propertyMultiValue = getMultiplePropertyValue(property.name); + if (!propertyMultiValue.isMultiDiffValue) { + console.log("setMultiDiffDragFunction is only supposed to be called in MultiDiff mode."); + return; + } + + let multiplier = property.data.multiplier !== undefined ? property.data.multiplier : 1; + + let applyDelta = changedDelta * multiplier; + console.log(applyDelta); + + if (selectedEntityIDs.size !== propertyMultiValue.values.length) { + console.log("selectedEntityIDs and propertyMultiValue got out of sync."); + return; + } + let updateObjects = {}; + const selectedEntityIDsArray = [...selectedEntityIDs]; + + for (var i = 0; i < selectedEntityIDsArray.length; ++i) { + let entityID = selectedEntityIDsArray[i]; + let updatedValue = propertyMultiValue.values[i] + applyDelta; + // FIXME: handle min/max per value? + updateObjects[entityID] = createPropertyUpdateObject(property.name, updatedValue); + // We need to store these so that we can send a full update on the dragEnd + mergeDeep(currentSelections[i].properties, updateObjects[entityID]); + } + + updateMultiDiffProperties(updateObjects, true); + }); elDraggableNumber.elInput.setAttribute("id", elementID); elProperty.appendChild(elDraggableNumber.elDiv); @@ -2341,7 +2511,7 @@ function createDropdownProperty(property, propertyID, elProperty) { } elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); - + elProperty.appendChild(elInput); return elInput; @@ -2360,8 +2530,12 @@ function createTextareaProperty(property, elProperty) { } elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); - + + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + elProperty.appendChild(elInput); + elProperty.appendChild(elMultiDiff); if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, true); @@ -2373,7 +2547,7 @@ function createTextareaProperty(property, elProperty) { function createIconProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - + elProperty.className = "value"; let elSpan = document.createElement('span'); @@ -2718,8 +2892,11 @@ function newJSONEditor() { showSaveUserDataButton(); } -function saveUserData() { - saveJSONUserData(true); +/** + * @param {Set.} [entityIDsToUpdate] Entity IDs to update userData for. + */ +function saveUserData(entityIDsToUpdate) { + saveJSONUserData(true, entityIDsToUpdate); } function setJSONError(property, isError) { @@ -2729,11 +2906,14 @@ function setJSONError(property, isError) { $propertyUserDataEditorStatus.text(isError ? 'Invalid JSON code - look for red X in your code' : ''); } -function setUserDataFromEditor(noUpdate) { - let json = null; +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] - Entity IDs to update userData for. + */ +function setUserDataFromEditor(noUpdate, entityIDsToUpdate) { let errorFound = false; try { - json = editor.get(); + editor.get(); } catch (e) { errorFound = true; } @@ -2748,7 +2928,7 @@ function setUserDataFromEditor(noUpdate) { if (noUpdate) { EventBridge.emitWebEvent( JSON.stringify({ - id: lastEntityID, + ids: [...entityIDsToUpdate], type: "saveUserData", properties: { userData: text @@ -2760,61 +2940,6 @@ function setUserDataFromEditor(noUpdate) { } } -function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, removeKeys) { - let propertyUpdate = {}; - let parsedData = {}; - let keysToBeRemoved = removeKeys ? removeKeys : []; - try { - if ($('#property-userData-editor').css('height') !== "0px") { - // if there is an expanded, we want to use its json. - parsedData = getEditorJSON(); - } else { - parsedData = JSON.parse(userDataElement.value); - } - } catch (e) { - // TODO: Should an alert go here? - } - - if (!(groupName in parsedData)) { - parsedData[groupName] = {}; - } - let keys = Object.keys(updateKeyPair); - keys.forEach(function (key) { - if (updateKeyPair[key] !== null && updateKeyPair[key] !== "null") { - if (updateKeyPair[key] instanceof Element) { - if (updateKeyPair[key].type === "checkbox") { - parsedData[groupName][key] = updateKeyPair[key].checked; - } else { - let val = isNaN(updateKeyPair[key].value) ? updateKeyPair[key].value : parseInt(updateKeyPair[key].value); - parsedData[groupName][key] = val; - } - } else { - parsedData[groupName][key] = updateKeyPair[key]; - } - } else if (defaults[key] !== null && defaults[key] !== "null") { - parsedData[groupName][key] = defaults[key]; - } - }); - keysToBeRemoved.forEach(function(key) { - if (parsedData[groupName].hasOwnProperty(key)) { - delete parsedData[groupName][key]; - } - }); - - if (Object.keys(parsedData[groupName]).length === 0) { - delete parsedData[groupName]; - } - if (Object.keys(parsedData).length > 0) { - propertyUpdate.userData = JSON.stringify(parsedData); - } else { - propertyUpdate.userData = ''; - } - - userDataElement.value = propertyUpdate.userData; - - updateProperties(propertyUpdate, false); -} - let editor = null; function createJSONEditor() { @@ -2837,8 +2962,6 @@ function createJSONEditor() { return; } $('#property-userData-button-save').attr('disabled', false); - - } }; editor = new JSONEditor(container, options); @@ -2896,10 +3019,6 @@ function setEditorJSON(json) { } } -function getEditorJSON() { - return editor.get(); -} - function deleteJSONEditor() { if (editor !== null) { setJSONError('userData', false); @@ -2910,8 +3029,12 @@ function deleteJSONEditor() { let savedJSONTimer = null; -function saveJSONUserData(noUpdate) { - setUserDataFromEditor(noUpdate); +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] Entity IDs to update userData for + */ +function saveJSONUserData(noUpdate, entityIDsToUpdate) { + setUserDataFromEditor(noUpdate, entityIDsToUpdate ? entityIDsToUpdate : selectedEntityIDs); $('#property-userData-saved').show(); $('#property-userData-button-save').attr('disabled', true); if (savedJSONTimer !== null) { @@ -2951,11 +3074,14 @@ function saveMaterialData() { saveJSONMaterialData(true); } -function setMaterialDataFromEditor(noUpdate) { - let json = null; +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] - Entity IDs to update materialData for. + */ +function setMaterialDataFromEditor(noUpdate, entityIDsToUpdate) { let errorFound = false; try { - json = materialEditor.get(); + materialEditor.get(); } catch (e) { errorFound = true; } @@ -2969,7 +3095,7 @@ function setMaterialDataFromEditor(noUpdate) { if (noUpdate) { EventBridge.emitWebEvent( JSON.stringify({ - id: lastEntityID, + ids: [...entityIDsToUpdate], type: "saveMaterialData", properties: { materialData: text @@ -2990,9 +3116,6 @@ function createJSONMaterialEditor() { mode: 'tree', modes: ['code', 'tree'], name: 'materialData', - onModeChange: function() { - $('.jsoneditor-poweredBy').remove(); - }, onError: function(e) { alert('JSON editor:' + e); }, @@ -3003,8 +3126,6 @@ function createJSONMaterialEditor() { return; } $('#property-materialData-button-save').attr('disabled', false); - - } }; materialEditor = new JSONEditor(container, options); @@ -3062,10 +3183,6 @@ function setMaterialEditorJSON(json) { } } -function getMaterialEditorJSON() { - return materialEditor.get(); -} - function deleteJSONMaterialEditor() { if (materialEditor !== null) { setJSONError('materialData', false); @@ -3076,8 +3193,12 @@ function deleteJSONMaterialEditor() { let savedMaterialJSONTimer = null; -function saveJSONMaterialData(noUpdate) { - setMaterialDataFromEditor(noUpdate); +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] - Entity IDs to update materialData for. + */ +function saveJSONMaterialData(noUpdate, entityIDsToUpdate) { + setMaterialDataFromEditor(noUpdate, entityIDsToUpdate ? entityIDsToUpdate : selectedEntityIDs); $('#property-materialData-saved').show(); $('#property-materialData-button-save').attr('disabled', true); if (savedMaterialJSONTimer !== null) { @@ -3100,13 +3221,12 @@ function bindAllNonJSONEditorElements() { if (e.target.id === "property-userData-button-edit" || e.target.id === "property-userData-button-clear" || e.target.id === "property-materialData-button-edit" || e.target.id === "property-materialData-button-clear") { return; - } else { - if ($('#property-userData-editor').css('height') !== "0px") { - saveUserData(); - } - if ($('#property-materialData-editor').css('height') !== "0px") { - saveMaterialData(); - } + } + if ($('#property-userData-editor').css('height') !== "0px") { + saveUserData(); + } + if ($('#property-materialData-editor').css('height') !== "0px") { + saveMaterialData(); } }); } @@ -3139,14 +3259,14 @@ function toggleDropdown(event) { } function closeAllDropdowns() { - elDropdowns = document.querySelectorAll("div.dropdown > dl"); + let elDropdowns = document.querySelectorAll("div.dropdown > dl"); for (let i = 0; i < elDropdowns.length; ++i) { elDropdowns[i].setAttribute('dropped', 'false'); } } function setDropdownValue(event) { - let dt = event.target.parentNode.parentNode.previousSibling; + let dt = event.target.parentNode.parentNode.previousSibling.previousSibling; dt.value = event.target.getAttribute("value"); dt.firstChild.textContent = event.target.textContent; @@ -3308,18 +3428,41 @@ function materialTargetPropertyUpdate(propertyValue) { } +function applyNumberPropertyModifiers(number, propertyData) { + const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + let result = number / multiplier; + if (propertyData.round !== undefined) { + result = Math.round(result * propertyData.round) / propertyData.round; + } + if (propertyData.decimals !== undefined) { + return result.toFixed(propertyData.decimals) + } + return result; +} + +const isSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); + + function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { - if (selections.length === 0) { - if (lastEntityID !== null) { - if (editor !== null) { - saveUserData(); - deleteJSONEditor(); - } - if (materialEditor !== null) { - saveMaterialData(); - deleteJSONMaterialEditor(); - } + const previouslySelectedEntityIDs = selectedEntityIDs; + currentSelections = selections; + selectedEntityIDs = new Set(selections.map(selection => selection.id)); + const multipleSelections = currentSelections.length > 1; + const hasSelectedEntityChanged = !isSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); + + // FIXME: do we really want to save userData/materialData here instead of saving it on the blur event of the json editor? + if (hasSelectedEntityChanged) { + if (editor !== null) { + saveUserData(previouslySelectedEntityIDs); } + if (materialEditor !== null) { + saveMaterialData(previouslySelectedEntityIDs); + } + } + + if (selections.length === 0) { + deleteJSONEditor(); + deleteJSONMaterialEditor(); resetProperties(); showGroupsForType("None"); @@ -3328,143 +3471,151 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { elIcon.innerText = NO_SELECTION; elIcon.style.display = 'inline-block'; - deleteJSONEditor(); getPropertyInputElement("userData").value = ""; showUserDataTextArea(); showSaveUserDataButton(); showNewJSONEditorButton(); - deleteJSONMaterialEditor(); getPropertyInputElement("materialData").value = ""; showMaterialDataTextArea(); showSaveMaterialDataButton(); showNewJSONMaterialEditorButton(); - disableProperties(); - } else if (selections.length > 1) { - deleteJSONEditor(); - deleteJSONMaterialEditor(); - - let ids = []; - let types = {}; - let numTypes = 0; - - for (let i = 0; i < selections.length; ++i) { - ids.push(selections[i].id); - let currentSelectedType = selections[i].properties.type; - if (types[currentSelectedType] === undefined) { - types[currentSelectedType] = 0; - numTypes += 1; - } - types[currentSelectedType]++; - } - - let type = "Multiple"; - if (numTypes === 1) { - type = selections[0].properties.type; - } - - resetProperties(); - showGroupsForType(type); - - let typeProperty = properties["type"]; - typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; - typeProperty.elSpan.style.display = "inline-block"; - disableProperties(); } else { - selectedEntityProperties = selections[0].properties; - - if (lastEntityID !== selectedEntityProperties.id && lastEntityID !== null) { - if (editor !== null) { - saveUserData(); - } - if (materialEditor !== null) { - saveMaterialData(); - } - } - - let hasSelectedEntityChanged = lastEntityID !== selectedEntityProperties.id; - if (!isPropertiesToolUpdate && !hasSelectedEntityChanged && document.hasFocus()) { // in case the selection has not changed and we still have focus on the properties page, // we will ignore the event. return; } + if (hasSelectedEntityChanged) { + let elServerScriptStatusOuter = document.getElementById('div-property-serverScriptStatus'); + elServerScriptStatusOuter.style.display = multipleSelections ? "none" : null; + if (!multipleSelections) { + resetServerScriptStatus(); + } + } - let doSelectElement = !hasSelectedEntityChanged; + const doSelectElement = !hasSelectedEntityChanged; - // the event bridge and json parsing handle our avatar id string differently. - lastEntityID = selectedEntityProperties.id; + // Get unique entity types, and convert the types Sphere and Box to Shape + const shapeTypes = ["Sphere", "Box"]; + const entityTypes = [...new Set(currentSelections.map(a => + shapeTypes.includes(a.properties.type) ? "Shape" : a.properties.type))]; - showGroupsForType(selectedEntityProperties.type); + showGroupsForTypes(entityTypes); - if (selectedEntityProperties.locked) { + const lockedMultiValue = getMultiplePropertyValue('locked'); + + if (lockedMultiValue.isMultiDiffValue || lockedMultiValue.value) { disableProperties(); - getPropertyInputElement("locked").removeAttribute('disabled'); + getPropertyInputElement('locked').removeAttribute('disabled'); } else { enableProperties(); disableSaveUserDataButton(); disableSaveMaterialDataButton() } - for (let propertyID in properties) { - let property = properties[propertyID]; - let propertyData = property.data; - let propertyName = property.name; - let propertyValue = getPropertyValue(propertyName); + const certificateIDMultiValue = getMultiplePropertyValue('certificateID'); + const hasCertifiedInSelection = certificateIDMultiValue.isMultiDiffValue || certificateIDMultiValue.value !== ""; - let isSubProperty = propertyData.subPropertyOf !== undefined; - if (propertyValue === undefined && !isSubProperty) { - continue; + Object.entries(properties).forEach(function([propertyID, property]) { + const propertyData = property.data; + const propertyName = property.name; + let propertyMultiValue = getMultiplePropertyValue(propertyName); + let isMultiDiffValue = propertyMultiValue.isMultiDiffValue; + let propertyValue = propertyMultiValue.value; + + const isSubProperty = propertyData.subPropertyOf !== undefined; + if (propertyValue === undefined && !isMultiDiffValue && !isSubProperty) { + return; } - if (propertyData.hideIfCertified) { - let shouldHide = selectedEntityProperties.certificateID !== ""; - if (shouldHide) { - propertyValue = "** Certified **"; - property.elInput.disabled = true; + if (propertyData.hideIfCertified && hasCertifiedInSelection) { + propertyValue = "** Certified **"; + property.elInput.disabled = true; + } + + if (propertyName === "type") { + propertyValue = entityTypes.length > 1 ? "Multiple" : propertyMultiValue.values[0]; + } + + if (!isMultiDiffValue) { + let isPropertyNotNumber = false; + switch (propertyData.type) { + case 'number': + case 'number-draggable': + isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; + break; + case 'rect': + case 'vec3': + case 'vec2': + isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; + break; + case 'color': + isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; + break; + } + if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { + propertyMultiValue = getMultiplePropertyValue(propertyData.fallbackProperty); + propertyValue = propertyMultiValue.value; + isMultiDiffValue = propertyMultiValue.value; } - } - - let isPropertyNotNumber = false; - switch (propertyData.type) { - case 'number': - case 'number-draggable': - isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; - break; - case 'rect': - case 'vec3': - case 'vec2': - isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; - break; - case 'color': - isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; - break; - } - if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { - propertyValue = getPropertyValue(propertyData.fallbackProperty); } switch (propertyData.type) { case 'string': { - property.elInput.value = propertyValue; - break; - } - case 'bool': { - let inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; - if (isSubProperty) { - let propertyValue = selectedEntityProperties[propertyData.subPropertyOf]; - let subProperties = propertyValue.split(","); - let subPropertyValue = subProperties.indexOf(propertyName) > -1; - property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; + if (isMultiDiffValue) { + if (propertyData.readOnly && propertyData.multiDisplayMode + && propertyData.multiDisplayMode === PROPERTY_MULTI_DISPLAY_MODE.COMMA_SEPARATED_VALUES) { + property.elInput.value = propertyMultiValue.values.join(", "); + } else { + property.elInput.classList.add('multi-diff'); + property.elInput.value = ""; + } } else { - property.elInput.checked = inverse ? !propertyValue : propertyValue; + property.elInput.classList.remove('multi-diff'); + property.elInput.value = propertyValue; } break; } + case 'bool': { + const inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; + if (isSubProperty) { + + let subPropertyMultiValue = getMultiplePropertyValue(propertyData.subPropertyOf); + let propertyValue = subPropertyMultiValue.value; + isMultiDiffValue = subPropertyMultiValue.isMultiDiffValue; + if (isMultiDiffValue) { + property.elInput.checked = false; + property.elInput.classList.add('multi-diff'); + } else { + let subProperties = propertyValue.split(","); + let subPropertyValue = subProperties.indexOf(propertyName) > -1; + property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; + property.elInput.classList.remove('multi-diff'); + } + + } else { + if (isMultiDiffValue) { + property.elInput.checked = false; + property.elInput.classList.add('multi-diff'); + } else { + property.elInput.checked = inverse ? !propertyValue : propertyValue; + property.elInput.classList.remove('multi-diff'); + } + } + + break; + } case 'number': { - property.elInput.value = propertyValue; + if (isMultiDiffValue) { + property.elInput.value = ""; + property.elInput.classList.add('multi-diff'); + } else { + property.elInput.value = propertyValue; + property.elInput.classList.remove('multi-diff'); + } break; } case 'number-draggable': { @@ -3473,7 +3624,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { if (propertyData.round !== undefined) { value = Math.round(value.round) / propertyData.round; } - property.elNumber.setValue(value); + property.elNumber.setValue(value, isMultiDiffValue); break; } case 'rect': @@ -3484,53 +3635,51 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; case 'vec3': case 'vec2': { - let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let valueX = propertyValue.x / multiplier; - let valueY = propertyValue.y / multiplier; - let valueZ = propertyValue.z / multiplier; - if (propertyData.round !== undefined) { - valueX = Math.round(valueX * propertyData.round) / propertyData.round; - valueY = Math.round(valueY * propertyData.round) / propertyData.round; - valueZ = Math.round(valueZ * propertyData.round) / propertyData.round; - } - if (propertyData.decimals !== undefined) { - property.elNumberX.setValue(valueX.toFixed(propertyData.decimals)); - property.elNumberY.setValue(valueY.toFixed(propertyData.decimals)); + if (isMultiDiffValue) { + property.elNumberX.setValue(0, true); + property.elNumberY.setValue(0, true); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(valueZ.toFixed(propertyData.decimals)); + property.elNumberZ.setValue(0, true); } } else { - property.elNumberX.setValue(valueX); - property.elNumberY.setValue(valueY); + property.elNumberX.setValue(applyNumberPropertyModifiers(propertyValue.x, propertyData), false); + property.elNumberY.setValue(applyNumberPropertyModifiers(propertyValue.y, propertyData), false); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(valueZ); + property.elNumberZ.setValue(applyNumberPropertyModifiers(propertyValue.z, propertyData), false); } } break; } case 'color': { - property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + - propertyValue.green + "," + - propertyValue.blue + ")"; + let displayColor = isMultiDiffValue ? propertyMultiValue.values[0] : propertyValue; + property.elColorPicker.style.backgroundColor = "rgb(" + displayColor.red + "," + + displayColor.green + "," + + displayColor.blue + ")"; if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') { // Set the color picker inactive before setting the color, // otherwise an update will be sent directly after setting it here. $(property.elColorPicker).attr('active', 'false'); colorPickers['#' + property.elementID].colpickSetColor({ - "r": propertyValue.red, - "g": propertyValue.green, - "b": propertyValue.blue + "r": displayColor.red, + "g": displayColor.green, + "b": displayColor.blue }); $(property.elColorPicker).attr('active', 'true'); } - property.elNumberR.setValue(propertyValue.red); - property.elNumberG.setValue(propertyValue.green); - property.elNumberB.setValue(propertyValue.blue); + property.elNumberR.setValue(displayColor.red); + property.elNumberG.setValue(displayColor.green); + property.elNumberB.setValue(displayColor.blue); break; } case 'dropdown': { - property.elInput.value = propertyValue; + if (isMultiDiffValue) { + property.elInput.classList.add('multi-diff'); + property.elInput.value = ""; + } else { + property.elInput.classList.remove('multi-diff'); + property.elInput.value = propertyValue; + } setDropdownText(property.elInput); break; } @@ -3565,22 +3714,20 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { showPropertyElement(propertyToShow, show); } } - } + }); updateVisibleSpaceModeProperties(); + let userDataMultiValue = getMultiplePropertyValue("userData"); + let json = null; - try { - json = JSON.parse(selectedEntityProperties.userData); - } catch (e) { - // normal text - deleteJSONEditor(); - getPropertyInputElement("userData").value = selectedEntityProperties.userData; - showUserDataTextArea(); - showNewJSONEditorButton(); - hideSaveUserDataButton(); - hideUserDataSaved(); + if (!userDataMultiValue.isMultiDiffValue) { + try { + json = JSON.parse(userDataMultiValue.value); + } catch (e) { + + } } if (json !== null) { if (editor === null) { @@ -3591,19 +3738,34 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { hideUserDataTextArea(); hideNewJSONEditorButton(); hideUserDataSaved(); + } else { + // normal text + deleteJSONEditor(); + + if (userDataMultiValue.isMultiDiffValue) { + // FIXME: set multiValue property + getPropertyInputElement("userData").value = ""; + } else { + // FIXME: unset multiValue property + getPropertyInputElement("userData").value = userDataMultiValue.value; + } + + showUserDataTextArea(); + showNewJSONEditorButton(); + hideSaveUserDataButton(); + hideUserDataSaved(); } + let materialDataMultiValue = getMultiplePropertyValue("materialData"); + + let materialJson = null; - try { - materialJson = JSON.parse(selectedEntityProperties.materialData); - } catch (e) { - // normal text - deleteJSONMaterialEditor(); - getPropertyInputElement("materialData").value = selectedEntityProperties.materialData; - showMaterialDataTextArea(); - showNewJSONMaterialEditorButton(); - hideSaveMaterialDataButton(); - hideMaterialDataSaved(); + if (!materialDataMultiValue.isMultiDiffValue) { + try { + materialJson = JSON.parse(materialDataMultiValue.value); + } catch (e) { + + } } if (materialJson !== null) { if (materialEditor === null) { @@ -3614,6 +3776,20 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { hideMaterialDataTextArea(); hideNewJSONMaterialEditorButton(); hideMaterialDataSaved(); + } else { + // normal text + deleteJSONMaterialEditor(); + if (materialDataMultiValue.isMultiDiffValue) { + // FIXME: set multiValue property + getPropertyInputElement("materialData").value = ""; + } else { + // FIXME: unset multiValue property + getPropertyInputElement("materialData").value = materialDataMultiValue.value; + } + showMaterialDataTextArea(); + showNewJSONMaterialEditorButton(); + hideSaveMaterialDataButton(); + hideMaterialDataSaved(); } if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { @@ -3789,7 +3965,7 @@ function loaded() { if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { data = JSON.parse(data); - if (data.type === "server_script_status") { + if (data.type === "server_script_status" && selectedEntityIDs.size === 1) { let elServerScriptError = document.getElementById("property-serverScripts-error"); let elServerScriptStatus = document.getElementById("property-serverScripts-status"); elServerScriptError.value = data.errorInfo; @@ -3876,7 +4052,7 @@ function loaded() { // Server Script Error let elServerScripts = getPropertyInputElement("serverScripts"); - elDiv = document.createElement('div'); + let elDiv = document.createElement('div'); elDiv.className = "property"; let elServerScriptError = document.createElement('textarea'); let serverScriptErrorElementID = 'property-serverScripts-error'; @@ -3993,6 +4169,10 @@ function loaded() { dt.addEventListener("click", toggleDropdown, true); dl.appendChild(dt); + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + dl.appendChild(elMultiDiff); + let span = document.createElement("span"); span.setAttribute("value", options[selectedOption].value); span.textContent = options[selectedOption].firstChild.textContent; From 8b24cf51de49a07dc57c50a0f2c977d4f1585fc8 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 9 Apr 2019 21:05:40 +0200 Subject: [PATCH 07/19] enhance the multi editing experience --- scripts/system/html/css/edit-style.css | 20 +- scripts/system/html/js/draggableNumber.js | 20 +- scripts/system/html/js/entityProperties.js | 583 +++++++++++++-------- 3 files changed, 392 insertions(+), 231 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 6190469c98..f8247a24d5 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -263,15 +263,17 @@ input[type="text"] { } input.multi-diff:not(:focus) + span.multi-diff, -.draggable-number.multi-diff span.multi-diff, +textarea.multi-diff:not(:focus) + span.multi-diff, +.draggable-number.multi-diff>input:not(:focus)+span.multi-diff, dl>dt.multi-diff:not(:focus) + span.multi-diff { visibility: visible; position: absolute; display: inline-block; z-index: 2; + top: 7.5px; left: 20px; width: 50px; - height: 40%; + height: 13px; background-image: linear-gradient(transparent 0%, transparent 10%, #afafaf 10%, #afafaf 20%, transparent 20%, transparent 45%, #afafaf 45%, #afafaf 55%, transparent 55%, transparent 80%, #afafaf 80%, #afafaf 90%, transparent 90%, transparent 100%); background-repeat: no-repeat; pointer-events: none; @@ -290,10 +292,8 @@ input.multi-diff:not(:focus)::-webkit-input-placeholder, input.multi-diff:not(:f left: 10px; } -.text, .url { +.text, .url, .texture, .textarea { position: relative; - display: flex; - align-items: center; } input[type="search"] { @@ -484,6 +484,15 @@ input.multi-diff[type=checkbox] + label:hover { background-image: url(data:image/png;base64,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) } +.rgb.fstuple .color-picker.multi-diff:after { + width: 20px; + height: 20px; + content: ' '; + background: darkgray; + display: flex; + clip-path: polygon(0 0, 0 100%, 100% 100%); +} + .icon-input input { position: relative; padding-left: 36px; @@ -575,7 +584,6 @@ input.multi-diff[type=checkbox] + label:hover { div.section-header, hr { display: flex; flex-flow: row nowrap; - padding: 10px 16px; font-family: Raleway-Regular; font-size: 12px; diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 96280b82f6..b09723a173 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -72,7 +72,6 @@ DraggableNumber.prototype = { this.lastMouseEvent = event; } if (this.dragging && this.lastMouseEvent) { - let initialValue = this.elInput.value; let changeDelta = event.clientX - this.lastMouseEvent.clientX; if (changeDelta !== 0) { if (this.multiDiffModeEnabled) { @@ -136,14 +135,16 @@ DraggableNumber.prototype = { this.setMultiDiff(isMultiDiff); } - if (!isMultiDiff) { - if (newValue !== "" && this.decimals !== undefined) { - this.elInput.value = parseFloat(newValue).toFixed(this.decimals); - } else { - this.elInput.value = newValue; - } - this.elText.firstChild.data = this.elInput.value; + if (isNaN(newValue)) { + throw newValue + " is not a number"; } + + if (newValue !== "" && this.decimals !== undefined) { + this.elInput.value = parseFloat(newValue).toFixed(this.decimals); + } else { + this.elInput.value = newValue; + } + this.elText.firstChild.data = this.elInput.value; }, setMultiDiff: function(isMultiDiff) { @@ -181,6 +182,9 @@ DraggableNumber.prototype = { keyPress: function(event) { if (event.keyCode === ENTER_KEY) { + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } this.inputBlur(); } }, diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index df244d435a..1dba7bb861 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1645,6 +1645,33 @@ function mergeDeep(target, ...sources) { return mergeDeep(target, ...sources); } +function deepEqual(a, b) { + if (a === b) { + return true; + } + + if (typeof(a) !== "object" || typeof(b) !== "object") { + return false; + } + + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + + for (let property in a) { + if (!a.hasOwnProperty(property)) { + continue; + } + if (!b.hasOwnProperty(property)) { + return false; + } + if (!deepEqual(a[property], b[property])) { + return false; + } + } + return true; +} + function createElementFromHTML(htmlString) { let elTemplate = document.createElement('template'); elTemplate.innerHTML = htmlString.trim(); @@ -1759,41 +1786,43 @@ function resetProperties() { } case 'number-draggable': { if (propertyData.defaultValue !== undefined) { - property.elNumber.setValue(propertyData.defaultValue); + property.elNumber.setValue(propertyData.defaultValue, false); } else { - property.elNumber.setValue(""); + property.elNumber.setValue("", false); } break; } case 'rect': { - property.elNumberX.setValue(""); - property.elNumberY.setValue(""); - property.elNumberWidth.setValue(""); - property.elNumberHeight.setValue(""); + property.elNumberX.setValue("", false); + property.elNumberY.setValue("", false); + property.elNumberWidth.setValue("", false); + property.elNumberHeight.setValue("", false); break; } case 'vec3': case 'vec2': { - property.elNumberX.setValue(""); - property.elNumberY.setValue(""); + property.elNumberX.setValue("", false); + property.elNumberY.setValue("", false); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(""); + property.elNumberZ.setValue("", false); } break; } case 'color': { property.elColorPicker.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - property.elNumberR.setValue(""); - property.elNumberG.setValue(""); - property.elNumberB.setValue(""); + property.elNumberR.setValue("", false); + property.elNumberG.setValue("", false); + property.elNumberB.setValue("", false); break; } case 'dropdown': { + property.elInput.classList.remove('multi-diff'); property.elInput.value = ""; setDropdownText(property.elInput); break; } case 'textarea': { + property.elInput.classList.remove('multi-diff'); property.elInput.value = ""; setTextareaScrolling(property.elInput); break; @@ -1803,6 +1832,7 @@ function resetProperties() { break; } case 'texture': { + property.elInput.classList.remove('multi-diff'); property.elInput.value = ""; property.elInput.imageLoad(property.elInput.value); break; @@ -1839,6 +1869,14 @@ function showGroupsForType(type) { showGroupsForTypes([type]); } +function getGroupsForTypes(types) { + return Object.keys(elGroups).filter((groupKey) => { + return types.map(type => GROUPS_PER_TYPE[type].includes(groupKey)).every(function (hasGroup) { + return hasGroup; + }); + }); +} + function showGroupsForTypes(types) { Object.entries(elGroups).forEach(([groupKey, elGroup]) => { if (types.map(type => GROUPS_PER_TYPE[type].includes(groupKey)).every(function (hasGroup) { return hasGroup; })) { @@ -1849,15 +1887,23 @@ function showGroupsForTypes(types) { }); } +const SUPPORTED_FALLBACK_TYPES = ['number', 'number-draggable', 'rect', 'vec3', 'vec2', 'color']; + function getMultiplePropertyValue(originalPropertyName) { // if this is a compound property name (i.e. animation.running) // then split it by . up to 3 times to find property value + + let propertyData = null; + if (properties[originalPropertyName] !== undefined) { + propertyData = properties[originalPropertyName].data; + } + let propertyValues = []; let splitPropertyName = originalPropertyName.split('.'); if (splitPropertyName.length > 1) { let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; - propertyValue = currentSelections.map(selection => { + propertyValues = currentSelections.map(selection => { let groupProperties = selection.properties[propertyGroupName]; if (groupProperties === undefined || groupProperties[propertyName] === undefined) { return undefined; @@ -1873,17 +1919,42 @@ function getMultiplePropertyValue(originalPropertyName) { propertyValues = currentSelections.map(selection => selection.properties[originalPropertyName]); } - let detailedValues = []; - propertyValues.forEach(function(propertyValue) { - if (typeof propertyValues === "object") { + if (propertyData !== null && propertyData.fallbackProperty !== undefined && + SUPPORTED_FALLBACK_TYPES.includes(propertyData.type)) { - } else { - detailedValues.push(propertyValue); + let fallbackMultiValue = null; + + for (let i = 0; i < propertyValues.length; ++i) { + let isPropertyNotNumber = false; + let propertyValue = propertyValues[i]; + if (propertyValue === undefined) { + continue; + } + switch (propertyData.type) { + case 'number': + case 'number-draggable': + isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; + break; + case 'rect': + case 'vec3': + case 'vec2': + isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; + break; + case 'color': + isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; + break; + } + if (isPropertyNotNumber) { + if (fallbackMultiValue === null) { + fallbackMultiValue = getMultiplePropertyValue(propertyData.fallbackProperty); + } + propertyValues[i] = fallbackMultiValue.values[i]; + } } - }); + } - const uniquePropertyValues = [...new Set(propertyValues)]; - const isMultiDiffValue = uniquePropertyValues.length > 1; + const firstValue = propertyValues[0]; + const isMultiDiffValue = !propertyValues.every((x) => deepEqual(firstValue, x)); if (isMultiDiffValue) { return { @@ -1894,12 +1965,73 @@ function getMultiplePropertyValue(originalPropertyName) { } return { - value: uniquePropertyValues[0], + value: propertyValues[0], values: propertyValues, isMultiDiffValue: false }; } + +function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { + let detailedValues = {}; + // Fixed numbers can't be easily averaged since they're strings, so lets keep an array of unmodified numbers + let unmodifiedValues = {}; + const DEFAULT_KEY = 0; + let uniqueKeys = new Set([]); + multiplePropertyValue.values.forEach(function(propertyValue) { + if (typeof propertyValue === "object") { + Object.entries(propertyValue).forEach(function([key, value]) { + if (!uniqueKeys.has(key)) { + uniqueKeys.add(key); + detailedValues[key] = []; + unmodifiedValues[key] = []; + } + detailedValues[key].push(applyInputNumberPropertyModifiers(value, propertyData)); + unmodifiedValues[key].push(value); + }); + } else { + if (!uniqueKeys.has(DEFAULT_KEY)) { + uniqueKeys.add(DEFAULT_KEY); + detailedValues[DEFAULT_KEY] = []; + unmodifiedValues[DEFAULT_KEY] = []; + } + detailedValues[DEFAULT_KEY].push(applyInputNumberPropertyModifiers(propertyValue, propertyData)); + unmodifiedValues[DEFAULT_KEY].push(propertyValue); + } + }); + let keys = [...uniqueKeys]; + + let subPropertyDiff = {}; + Object.entries(detailedValues).forEach(function([key, value]) { + subPropertyDiff[key] = [...new Set(value)].length > 1; + }); + + let averagePerSubProperty = {}; + Object.entries(unmodifiedValues).forEach(function([key, value]) { + let average = value.reduce((a, b) => a + b) / value.length; + averagePerSubProperty[key] = applyInputNumberPropertyModifiers(average, propertyData); + }); + + return { + keys, + subPropertyDiff, + averagePerSubProperty + }; +} + +function getDetailedSubPropertyMVPDiff(multiplePropertyValue, subPropertyName) { + let isChecked = false; + let checkedValues = multiplePropertyValue.values.map((value) => value.split(",").includes(subPropertyName)); + let isMultiDiff = !checkedValues.every(value => value === checkedValues[0]); + if (!isMultiDiff) { + isChecked = checkedValues[0]; + } + return { + isChecked, + isMultiDiff + } +} + function updateVisibleSpaceModeProperties() { for (let propertyID in properties) { if (properties.hasOwnProperty(propertyID)) { @@ -1985,11 +2117,6 @@ function updateMultiDiffProperties(propertiesMapToUpdate, onlyUpdateEntity) { propertiesMap: propertiesMapToUpdate, onlyUpdateEntities: onlyUpdateEntity })); - console.log(JSON.stringify({ - type: "update", - propertiesMap: propertiesMapToUpdate, - onlyUpdateEntities: onlyUpdateEntity - })) } function createEmitTextPropertyUpdateFunction(property) { @@ -2020,7 +2147,7 @@ function createDragEndFunction(property) { let updateObjects = []; const selectedEntityIDsArray = [...selectedEntityIDs]; - for (var i = 0; i < selectedEntityIDsArray.length; ++i) { + for (let i = 0; i < selectedEntityIDsArray.length; ++i) { let entityID = selectedEntityIDsArray[i]; updateObjects.push({ entityIDs: [entityID], @@ -2039,53 +2166,46 @@ function createDragEndFunction(property) { function createEmitNumberPropertyUpdateFunction(property) { return function() { - let multiplier = property.data.multiplier; - if (multiplier === undefined) { - multiplier = 1; - } - let value = parseFloat(this.value) * multiplier; + let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); updateProperty(property.name, value, property.isParticleProperty); }; } -function createEmitVec2PropertyUpdateFunction(property) { - return function () { - let multiplier = property.data.multiplier; - if (multiplier === undefined) { - multiplier = 1; - } - let newValue = { - x: property.elNumberX.elInput.value * multiplier, - y: property.elNumberY.elInput.value * multiplier - }; - updateProperty(property.name, newValue, property.isParticleProperty); - }; -} - -function createEmitVec3PropertyUpdateFunction(property) { +function createEmitNumberSubPropertyUpdateFunction(property, subProperty) { return function() { - let multiplier = property.data.multiplier; - if (multiplier === undefined) { - multiplier = 1; - } - let newValue = { - x: property.elNumberX.elInput.value * multiplier, - y: property.elNumberY.elInput.value * multiplier, - z: property.elNumberZ.elInput.value * multiplier - }; - updateProperty(property.name, newValue, property.isParticleProperty); - }; -} + let propertyMultiValue = getMultiplePropertyValue(property.name); + let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); -function createEmitRectPropertyUpdateFunction(property) { - return function() { - let newValue = { - x: property.elNumberX.elInput.value, - y: property.elNumberY.elInput.value, - width: property.elNumberWidth.elInput.value, - height: property.elNumberHeight.elInput.value, - }; - updateProperty(property.name, newValue, property.isParticleProperty); + if (propertyMultiValue.isMultiDiffValue) { + let updateObjects = []; + const selectedEntityIDsArray = [...selectedEntityIDs]; + + for (let i = 0; i < selectedEntityIDsArray.length; ++i) { + let entityID = selectedEntityIDsArray[i]; + + let propertyObject = propertyMultiValue.values[i]; + propertyObject[subProperty] = value; + + let updateObject = createPropertyUpdateObject(property.name, propertyObject); + updateObjects.push({ + entityIDs: [entityID], + properties: updateObject, + }); + + mergeDeep(currentSelections[i].properties, updateObject); + } + + // only update the entity property value itself if in the middle of dragging + // prevent undo command push, saving new property values, and property update + // callback until drag is complete (additional update sent via dragEnd callback) + let onlyUpdateEntity = properties[property.name] && properties[property.name].dragging === true; + updateMultiDiffProperties(updateObjects, onlyUpdateEntity); + console.log('updateMultiDiffProperties'); + } else { + let propertyValue = propertyMultiValue.value; + propertyValue[subProperty] = value; + updateProperty(property.name, propertyValue, property.isParticleProperty); + } }; } @@ -2105,16 +2225,34 @@ function emitColorPropertyUpdate(propertyName, red, green, blue, isParticlePrope updateProperty(propertyName, newValue, isParticleProperty); } -function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString, isParticleProperty) { - if (subPropertyElement.checked) { - if (propertyValue.indexOf(subPropertyString)) { - propertyValue += subPropertyString + ','; - } - } else { - // We've unchecked, so remove - propertyValue = propertyValue.replace(subPropertyString + ",", ""); +function toggleBooleanCSV(inputCSV, property, enable) { + let values = inputCSV.split(","); + if (enable && !values.includes(property)) { + values.push(property); + } else if (!enable && values.includes(property)) { + values = values.filter(value => value !== property); + } + return values.join(","); +} + +function updateCheckedSubProperty(propertyName, propertyMultiValue, subPropertyElement, subPropertyString, isParticleProperty) { + if (propertyMultiValue.isMultiDiffValue) { + let updateObjects = []; + const selectedEntityIDsArray = [...selectedEntityIDs]; + + for (let i = 0; i < selectedEntityIDsArray.length; ++i) { + let newValue = toggleBooleanCSV(propertyMultiValue.values[i], subPropertyString, subPropertyElement.checked); + updateObjects.push({ + entityIDs: [selectedEntityIDsArray[i]], + properties: createPropertyUpdateObject(propertyName, newValue), + }); + } + + updateMultiDiffProperties(updateObjects); + } else { + updateProperty(propertyName, toggleBooleanCSV(propertyMultiValue.value, subPropertyString, subPropertyElement.checked), + isParticleProperty); } - updateProperty(propertyName, propertyValue, isParticleProperty); } /** @@ -2181,7 +2319,7 @@ function createBoolProperty(property, elProperty) { let subPropertyMultiValue = getMultiplePropertyValue(subPropertyOf); updateCheckedSubProperty(subPropertyOf, - subPropertyMultiValue.value, + subPropertyMultiValue, elInput, propertyName, property.isParticleProperty); }); } else { @@ -2245,26 +2383,14 @@ function updateNumberMinMax(property) { } } -function createNumberDraggableProperty(property, elProperty) { - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className += " draggable-number-container"; - - let dragStartFunction = createDragStartFunction(property); - let dragEndFunction = createDragEndFunction(property); - let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, - propertyData.decimals, dragStartFunction, dragEndFunction); - - let defaultValue = propertyData.defaultValue; - if (defaultValue !== undefined) { - elDraggableNumber.elInput.value = defaultValue; - } - - let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); - elDraggableNumber.setValueChangeFunction(valueChangeFunction); - - elDraggableNumber.setMultiDiffDragFunction((changedDelta) => { +/** + * + * @param {object} property - property update on drag + * @param {string} [subProperty] - subProperty to update on drag (e.g. enter 'x' to just update position.x) + * @returns {Function} + */ +function createMultiDiffDragFunction(property, subProperty) { + return function(changedDelta) { let propertyMultiValue = getMultiplePropertyValue(property.name); if (!propertyMultiValue.isMultiDiffValue) { console.log("setMultiDiffDragFunction is only supposed to be called in MultiDiff mode."); @@ -2274,26 +2400,58 @@ function createNumberDraggableProperty(property, elProperty) { let multiplier = property.data.multiplier !== undefined ? property.data.multiplier : 1; let applyDelta = changedDelta * multiplier; - console.log(applyDelta); if (selectedEntityIDs.size !== propertyMultiValue.values.length) { console.log("selectedEntityIDs and propertyMultiValue got out of sync."); return; } - let updateObjects = {}; + let updateObjects = []; const selectedEntityIDsArray = [...selectedEntityIDs]; - for (var i = 0; i < selectedEntityIDsArray.length; ++i) { + for (let i = 0; i < selectedEntityIDsArray.length; ++i) { let entityID = selectedEntityIDsArray[i]; - let updatedValue = propertyMultiValue.values[i] + applyDelta; - // FIXME: handle min/max per value? - updateObjects[entityID] = createPropertyUpdateObject(property.name, updatedValue); + + let updatedValue; + if (subProperty !== undefined) { + let objectToUpdate = propertyMultiValue.values[i]; + objectToUpdate[subProperty] += applyDelta; + updatedValue = objectToUpdate; + } else { + updatedValue = propertyMultiValue.values[i] + applyDelta; + } + let propertiesUpdate = createPropertyUpdateObject(property.name, updatedValue); + updateObjects.push({ + entityIDs: [entityID], + properties: propertiesUpdate + }); // We need to store these so that we can send a full update on the dragEnd - mergeDeep(currentSelections[i].properties, updateObjects[entityID]); + mergeDeep(currentSelections[i].properties, propertiesUpdate); } updateMultiDiffProperties(updateObjects, true); - }); + } +} + +function createNumberDraggableProperty(property, elProperty) { + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className += " draggable-number-container"; + + let dragStartFunction = createDragStartFunction(property); + let dragEndFunction = createDragEndFunction(property); + let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, + propertyData.decimals, dragStartFunction, dragEndFunction); + + let defaultValue = propertyData.defaultValue; + if (defaultValue !== undefined) { + elDraggableNumber.elInput.value = defaultValue; + } + + let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); + elDraggableNumber.setValueChangeFunction(valueChangeFunction); + + elDraggableNumber.setMultiDiffDragFunction(createMultiDiffDragFunction(property)); elDraggableNumber.elInput.setAttribute("id", elementID); elProperty.appendChild(elDraggableNumber.elDiv); @@ -2334,11 +2492,15 @@ function createRectProperty(property, elProperty) { elWidthHeightRow.appendChild(elNumberWidth.elDiv); elWidthHeightRow.appendChild(elNumberHeight.elDiv); - let valueChangeFunction = createEmitRectPropertyUpdateFunction(property); - elNumberX.setValueChangeFunction(valueChangeFunction); - elNumberY.setValueChangeFunction(valueChangeFunction); - elNumberWidth.setValueChangeFunction(valueChangeFunction); - elNumberHeight.setValueChangeFunction(valueChangeFunction); + elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); + elNumberWidth.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'width')); + elNumberHeight.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'height')); + + elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); + elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); + elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'width')); + elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'height')); let elResult = []; elResult[RECT_ELEMENTS.X_NUMBER] = elNumberX; @@ -2368,11 +2530,14 @@ function createVec3Property(property, elProperty) { elProperty.appendChild(elNumberX.elDiv); elProperty.appendChild(elNumberY.elDiv); elProperty.appendChild(elNumberZ.elDiv); - - let valueChangeFunction = createEmitVec3PropertyUpdateFunction(property); - elNumberX.setValueChangeFunction(valueChangeFunction); - elNumberY.setValueChangeFunction(valueChangeFunction); - elNumberZ.setValueChangeFunction(valueChangeFunction); + + elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); + elNumberZ.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'z')); + + elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); + elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); + elNumberZ.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'z')); let elResult = []; elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; @@ -2395,10 +2560,12 @@ function createVec2Property(property, elProperty) { let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); elProperty.appendChild(elNumberX.elDiv); elProperty.appendChild(elNumberY.elDiv); - - let valueChangeFunction = createEmitVec2PropertyUpdateFunction(property); - elNumberX.setValueChangeFunction(valueChangeFunction); - elNumberY.setValueChangeFunction(valueChangeFunction); + + elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); + + elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); + elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); let elResult = []; elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; @@ -2462,7 +2629,6 @@ function createColorProperty(property, elProperty) { color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { - console.log("Showing"); // The original color preview within the picker needs to be updated on show because // prior to the picker being shown we don't have access to the selections' starting color. colorPickers[colorPickerID].colpickSetColor({ @@ -2546,7 +2712,6 @@ function createTextareaProperty(property, elProperty) { function createIconProperty(property, elProperty) { let elementID = property.elementID; - let propertyData = property.data; elProperty.className = "value"; @@ -2574,6 +2739,7 @@ function createTextureProperty(property, elProperty) { elInput.setAttribute("type", "text"); let imageLoad = function(url) { + elDiv.style.display = null; if (url.slice(0, 5).toLowerCase() === "atp:/") { elImage.src = ""; elImage.style.display = "none"; @@ -2595,12 +2761,18 @@ function createTextureProperty(property, elProperty) { } }; elInput.imageLoad = imageLoad; + elInput.setMultipleValues = function() { + elDiv.style.display = "none"; + }; elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); elInput.addEventListener('change', function(ev) { imageLoad(ev.target.value); }); elProperty.appendChild(elInput); + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + elProperty.appendChild(elMultiDiff); elProperty.appendChild(elDiv); let elResult = []; @@ -2630,7 +2802,7 @@ function createDynamicMultiselectProperty(property, elProperty) { let elDivOptions = document.createElement('div'); elDivOptions.setAttribute("id", elementID + "-options"); - elDivOptions.style = "overflow-y:scroll;max-height:160px;" + elDivOptions.style = "overflow-y:scroll;max-height:160px;"; let elDivButtons = document.createElement('div'); elDivButtons.setAttribute("id", elDivOptions.getAttribute("id") + "-buttons"); @@ -2883,6 +3055,7 @@ function clearUserData() { } function newJSONEditor() { + getPropertyInputElement("userData").classList.remove('multi-diff'); deleteJSONEditor(); createJSONEditor(); let data = {}; @@ -2949,9 +3122,6 @@ function createJSONEditor() { mode: 'tree', modes: ['code', 'tree'], name: 'userData', - onModeChange: function() { - $('.jsoneditor-poweredBy').remove(); - }, onError: function(e) { alert('JSON editor:' + e); }, @@ -3061,6 +3231,7 @@ function clearMaterialData() { } function newJSONMaterialEditor() { + getPropertyInputElement("materialData").classList.remove('multi-diff'); deleteJSONMaterialEditor(); createJSONMaterialEditor(); let data = {}; @@ -3387,7 +3558,7 @@ function sendMaterialTargetProperty() { if (elInput.checked) { let targetID = elInput.getAttribute("targetID"); if (elInput.getAttribute("isMaterialName") === "true") { - materialTargetList.push("mat::" + targetID); + materialTargetList.push(MATERIAL_PREFIX_STRING + targetID); } else { materialTargetList.push(targetID); } @@ -3419,7 +3590,7 @@ function materialTargetPropertyUpdate(propertyValue) { let targetID = elInput.getAttribute("targetID"); let materialTargetName = targetID; if (elInput.getAttribute("isMaterialName") === "true") { - materialTargetName = "mat::" + targetID; + materialTargetName = MATERIAL_PREFIX_STRING + targetID; } elInput.checked = materialTargets.indexOf(materialTargetName) >= 0; } @@ -3427,10 +3598,8 @@ function materialTargetPropertyUpdate(propertyValue) { elDivOptions.propertyValue = propertyValue; } - -function applyNumberPropertyModifiers(number, propertyData) { - const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let result = number / multiplier; +function roundAndFixNumber(number, propertyData) { + let result = number; if (propertyData.round !== undefined) { result = Math.round(result * propertyData.round) / propertyData.round; } @@ -3440,6 +3609,16 @@ function applyNumberPropertyModifiers(number, propertyData) { return result; } +function applyInputNumberPropertyModifiers(number, propertyData) { + const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + return roundAndFixNumber(number / multiplier, propertyData); +} + +function applyOutputNumberPropertyModifiers(number, propertyData) { + const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + return roundAndFixNumber(number * multiplier, propertyData); +} + const isSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); @@ -3503,6 +3682,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { const entityTypes = [...new Set(currentSelections.map(a => shapeTypes.includes(a.properties.type) ? "Shape" : a.properties.type))]; + const shownGroups = getGroupsForTypes(entityTypes); showGroupsForTypes(entityTypes); const lockedMultiValue = getMultiplePropertyValue('locked'); @@ -3531,6 +3711,15 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { return; } + if (!shownGroups.includes(property.group_id)) { + const WANT_DEBUG_SHOW_HIDDEN_FROM_GROUPS = false; + if (WANT_DEBUG_SHOW_HIDDEN_FROM_GROUPS) { + console.log("Skipping property " + property.data.label + " [" + property.name + + "] from hidden group " + property.group_id); + } + return; + } + if (propertyData.hideIfCertified && hasCertifiedInSelection) { propertyValue = "** Certified **"; property.elInput.disabled = true; @@ -3540,29 +3729,6 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { propertyValue = entityTypes.length > 1 ? "Multiple" : propertyMultiValue.values[0]; } - if (!isMultiDiffValue) { - let isPropertyNotNumber = false; - switch (propertyData.type) { - case 'number': - case 'number-draggable': - isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; - break; - case 'rect': - case 'vec3': - case 'vec2': - isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; - break; - case 'color': - isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; - break; - } - if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { - propertyMultiValue = getMultiplePropertyValue(propertyData.fallbackProperty); - propertyValue = propertyMultiValue.value; - isMultiDiffValue = propertyMultiValue.value; - } - } - switch (propertyData.type) { case 'string': { if (isMultiDiffValue) { @@ -3582,13 +3748,13 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { case 'bool': { const inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; if (isSubProperty) { - let subPropertyMultiValue = getMultiplePropertyValue(propertyData.subPropertyOf); let propertyValue = subPropertyMultiValue.value; isMultiDiffValue = subPropertyMultiValue.isMultiDiffValue; if (isMultiDiffValue) { - property.elInput.checked = false; - property.elInput.classList.add('multi-diff'); + let detailedSubProperty = getDetailedSubPropertyMVPDiff(subPropertyMultiValue, propertyName); + property.elInput.checked = detailedSubProperty.isChecked; + property.elInput.classList.toggle('multi-diff', detailedSubProperty.isMultiDiff); } else { let subProperties = propertyValue.split(","); let subPropertyValue = subProperties.indexOf(propertyName) > -1; @@ -3599,11 +3765,10 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } else { if (isMultiDiffValue) { property.elInput.checked = false; - property.elInput.classList.add('multi-diff'); } else { property.elInput.checked = inverse ? !propertyValue : propertyValue; - property.elInput.classList.remove('multi-diff'); } + property.elInput.classList.toggle('multi-diff', isMultiDiffValue); } break; @@ -3619,42 +3784,35 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; } case 'number-draggable': { - let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let value = propertyValue / multiplier; - if (propertyData.round !== undefined) { - value = Math.round(value.round) / propertyData.round; - } - property.elNumber.setValue(value, isMultiDiffValue); + let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); + property.elNumber.setValue(detailedNumberDiff.averagePerSubProperty[0], detailedNumberDiff.subPropertyDiff[0]); break; } - case 'rect': - property.elNumberX.setValue(propertyValue.x); - property.elNumberY.setValue(propertyValue.y); - property.elNumberWidth.setValue(propertyValue.width); - property.elNumberHeight.setValue(propertyValue.height); + case 'rect': { + let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); + property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.subPropertyDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.subPropertyDiff.y); + property.elNumberWidth.setValue(detailedNumberDiff.averagePerSubProperty.width, detailedNumberDiff.subPropertyDiff.width); + property.elNumberHeight.setValue(detailedNumberDiff.averagePerSubProperty.height, detailedNumberDiff.subPropertyDiff.height); break; + } case 'vec3': case 'vec2': { - if (isMultiDiffValue) { - property.elNumberX.setValue(0, true); - property.elNumberY.setValue(0, true); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(0, true); - } - } else { - property.elNumberX.setValue(applyNumberPropertyModifiers(propertyValue.x, propertyData), false); - property.elNumberY.setValue(applyNumberPropertyModifiers(propertyValue.y, propertyData), false); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(applyNumberPropertyModifiers(propertyValue.z, propertyData), false); - } + let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); + property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.subPropertyDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.subPropertyDiff.y); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(detailedNumberDiff.averagePerSubProperty.z, detailedNumberDiff.subPropertyDiff.z); } break; } case 'color': { - let displayColor = isMultiDiffValue ? propertyMultiValue.values[0] : propertyValue; + let displayColor = propertyMultiValue.isMultiDiffValue ? propertyMultiValue.values[0] : propertyValue; property.elColorPicker.style.backgroundColor = "rgb(" + displayColor.red + "," + displayColor.green + "," + displayColor.blue + ")"; + property.elColorPicker.classList.toggle('multi-diff', propertyMultiValue.isMultiDiffValue); + if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') { // Set the color picker inactive before setting the color, // otherwise an update will be sent directly after setting it here. @@ -3673,13 +3831,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; } case 'dropdown': { - if (isMultiDiffValue) { - property.elInput.classList.add('multi-diff'); - property.elInput.value = ""; - } else { - property.elInput.classList.remove('multi-diff'); - property.elInput.value = propertyValue; - } + property.elInput.classList.toggle('multi-diff', isMultiDiffValue); + property.elInput.value = isMultiDiffValue ? "" : propertyValue; setDropdownText(property.elInput); break; } @@ -3694,12 +3847,17 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; } case 'texture': { - property.elInput.value = propertyValue; - property.elInput.imageLoad(property.elInput.value); + property.elInput.value = isMultiDiffValue ? "" : propertyValue; + property.elInput.classList.toggle('multi-diff'); + if (isMultiDiffValue) { + property.elInput.setMultipleValues(); + } else { + property.elInput.imageLoad(property.elInput.value); + } break; } case 'dynamic-multiselect': { - if (property.data.propertyUpdate) { + if (!isMultiDiffValue && property.data.propertyUpdate) { property.data.propertyUpdate(propertyValue); } break; @@ -3718,9 +3876,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { updateVisibleSpaceModeProperties(); - let userDataMultiValue = getMultiplePropertyValue("userData"); - + let userDataTextArea = getPropertyInputElement("userData"); let json = null; if (!userDataMultiValue.isMultiDiffValue) { try { @@ -3733,6 +3890,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { if (editor === null) { createJSONEditor(); } + userDataTextArea.classList.remove('multi-diff'); setEditorJSON(json); showSaveUserDataButton(); hideUserDataTextArea(); @@ -3741,14 +3899,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } else { // normal text deleteJSONEditor(); - - if (userDataMultiValue.isMultiDiffValue) { - // FIXME: set multiValue property - getPropertyInputElement("userData").value = ""; - } else { - // FIXME: unset multiValue property - getPropertyInputElement("userData").value = userDataMultiValue.value; - } + userDataTextArea.classList.toggle('multi-diff', userDataMultiValue.isMultiDiffValue); + userDataTextArea.value = userDataMultiValue.isMultiDiffValue ? "" : userDataMultiValue.value; showUserDataTextArea(); showNewJSONEditorButton(); @@ -3757,8 +3909,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } let materialDataMultiValue = getMultiplePropertyValue("materialData"); - - + let materialDataTextArea = getPropertyInputElement("materialData"); let materialJson = null; if (!materialDataMultiValue.isMultiDiffValue) { try { @@ -3771,6 +3922,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { if (materialEditor === null) { createJSONMaterialEditor(); } + materialDataTextArea.classList.remove('multi-diff'); setMaterialEditorJSON(materialJson); showSaveMaterialDataButton(); hideMaterialDataTextArea(); @@ -3779,20 +3931,15 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } else { // normal text deleteJSONMaterialEditor(); - if (materialDataMultiValue.isMultiDiffValue) { - // FIXME: set multiValue property - getPropertyInputElement("materialData").value = ""; - } else { - // FIXME: unset multiValue property - getPropertyInputElement("materialData").value = materialDataMultiValue.value; - } + materialDataTextArea.classList.toggle('multi-diff', materialDataMultiValue.isMultiDiffValue); + materialDataTextArea.value = materialDataMultiValue.isMultiDiffValue ? "" : materialDataMultiValue.value; showMaterialDataTextArea(); showNewJSONMaterialEditorButton(); hideSaveMaterialDataButton(); hideMaterialDataSaved(); } - if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { + if (hasSelectedEntityChanged && selections.length === 1 && entityTypes[0] === "Material") { requestMaterialTarget(); } @@ -3912,6 +4059,7 @@ function loaded() { property.isParticleProperty = group.id.includes("particles"); property.elContainer = elContainer; property.spaceMode = propertySpaceMode; + property.group_id = group.id; let elLabel = createElementFromHTML(`
${innerPropertyData.label}
`); createAppTooltip.registerTooltipElement(elLabel, propertyID, propertyName); @@ -3930,6 +4078,7 @@ function loaded() { property.isParticleProperty = group.id.includes("particles"); property.elContainer = elContainer; property.spaceMode = propertySpaceMode; + property.group_id = group.id; if (property.type !== 'placeholder') { properties[propertyID] = property; @@ -4000,13 +4149,13 @@ function loaded() { if (propertyRange !== undefined) { let propertyData = properties[property].data; let multiplier = propertyData.multiplier; - if (propertyData.min === undefined && propertyRange.minimum != "") { + if (propertyData.min === undefined && propertyRange.minimum !== "") { propertyData.min = propertyRange.minimum; if (multiplier !== undefined) { propertyData.min /= multiplier; } } - if (propertyData.max === undefined && propertyRange.maximum != "") { + if (propertyData.max === undefined && propertyRange.maximum !== "") { propertyData.max = propertyRange.maximum; if (multiplier !== undefined) { propertyData.max /= multiplier; From c283c062c1e8f6bf05c62d642713558e6a90d715 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 12 Apr 2019 19:23:15 +0200 Subject: [PATCH 08/19] CR changes --- scripts/system/html/css/edit-style.css | 8 +- scripts/system/html/js/draggableNumber.js | 18 ++- scripts/system/html/js/entityProperties.js | 123 ++++++++++++--------- 3 files changed, 84 insertions(+), 65 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index f8247a24d5..470e57ad6d 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -272,7 +272,9 @@ dl>dt.multi-diff:not(:focus) + span.multi-diff { z-index: 2; top: 7.5px; left: 20px; - width: 50px; + max-width: 50px; + min-width: 10px; + width: 50%; height: 13px; background-image: linear-gradient(transparent 0%, transparent 10%, #afafaf 10%, #afafaf 20%, transparent 20%, transparent 45%, #afafaf 45%, #afafaf 55%, transparent 55%, transparent 80%, #afafaf 80%, #afafaf 90%, transparent 90%, transparent 100%); background-repeat: no-repeat; @@ -478,10 +480,10 @@ input[type=checkbox]:checked + label:hover { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEySURBVDhPnZLPSsNAEMa/XVPBCE0RhNy0OarP4Av4AD6JB0GwVBA8efBBxHsgh4CQswcRoUIpiIpVAm3zZ5M4szFSbQPBH3xkJvNNZskOer2eLIriKM/ze1JOcS1UHmdZduF5ngEKjr/fN4Z6+oKerwA2gxC4HAFPEWVLsAzgZAvYt3Q6Enw6jg7uBAaTFMNwhpnKdbXCkAJdy8ROu4XrXW2HTJIErHcFDD6nC02Mom8PwymeE2gvS0ZRBBaTlsOXEmdlrfLLOI7Bakrl/zWxCT8T/904f9QW/b06qtrCUdtFCqdjYs2Q2jAPX8c2XQd7Kr/wfV8vwIPs4Ga1ixe5Xrr/YFLTYfKIvWzM6ZtwXZdX7lxXG0L+sxXHcW5t254opRzawQ0S72+dPmjTroIgOP0CQSMt5LDn1T8AAAAASUVORK5CYII=); } input.multi-diff[type=checkbox] + label { - background-image: url(data:image/png;base64,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) + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFbSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyALkF4IE34gqM9zU9WT4wicG4mIA1l/fGIyOL2EQeP8EZEAiC5AQBUlcMQ5ieMXIwfDo9SeG73/+gRXDAAsTI4Pd9wdgTVAgw/Tv3z8GEP7Jwctw78M3DE0goPr6BoPludVgdTAM1wgCv//9B9PIQOPNDYaAGxtRNIEw03+gYhDGBtSBNgVc3wiWR8dM//4DTQBidKD++jqD//X1YDlsGMWpMKD26jqD79V1GM5DxihOZQWGntqrawy+V9ZiOA0dw21k/f6JwerzHQbvS2swTMeGGfPz8l8BLRP9KizDwP0WHk+EwGum/3//94M8y/nmEdZAwIb//vs7g/nk6ZPHzE3NvwITrxLDfwYhIAamZpz4PcM/hslXrl6pBwAmfz5iaAlAuAAAAABJRU5ErkJggg==); } input.multi-diff[type=checkbox] + label:hover { - background-image: url(data:image/png;base64,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) + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFPSURBVDhPjZJBSwJBHMXfrG6rWEkl1MFDGOihDn2JIIrqc3QJunbyFhUkRieD+hYepWteuxctXiJ1Q5xmdmZ3bWZTUHezfvAu/3lv3n+HRblcTrbb7fN+v/8eBMFgFpxz13Gcu3q9bqHb7V4M5/9GhatE3cIsy0o99YBKC3jliCWbBK43gK0MoDI9otfTB/vPBC9Uwu4xMC8IzSOSBsFxIYNqMTGcAIYQAlodD3j5/IqENIc5gqt1P/SNZKhaXR0a5E/5BEcrwH1xEHrGZbiuC604DpZ81AoiPJ/WROM4e4sSt3kaaRopNrg7z1FZdSLmcU2saqrX20lTXC5/RFabFmk2m+GLnBnbWJMOThJv4SV/QRqNBjNNM9UiGeQHdDiejZSSG5TSG71zjnVivyVOKlNLlEqlx+xCds7zvU31G6Z938dvEq4QjLMH27ZPvwHFVYQr3h7uHwAAAABJRU5ErkJggg==); } .rgb.fstuple .color-picker.multi-diff:after { diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index b09723a173..d76874d4d9 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -72,21 +72,17 @@ DraggableNumber.prototype = { this.lastMouseEvent = event; } if (this.dragging && this.lastMouseEvent) { - let changeDelta = event.clientX - this.lastMouseEvent.clientX; - if (changeDelta !== 0) { + let dragDelta = event.clientX - this.lastMouseEvent.clientX; + if (dragDelta !== 0) { if (this.multiDiffModeEnabled) { if (this.multiDiffDragFunction) { - this.multiDiffDragFunction(changeDelta * this.step); + this.multiDiffDragFunction(dragDelta * this.step); } } else { - while (changeDelta !== 0) { - if (changeDelta > 0) { - this.elInput.stepUp(); - --changeDelta; - } else { - this.elInput.stepDown(); - ++changeDelta; - } + if (dragDelta > 0) { + this.elInput.stepUp(dragDelta); + } else { + this.elInput.stepDown(-dragDelta); } this.inputChange(); if (this.valueChangeFunction) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 1dba7bb861..6a93a84dca 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -14,21 +14,28 @@ const DEGREES_TO_RADIANS = Math.PI / 180.0; const NO_SELECTION = ","; -const PROPERTY_SPACE_MODE = { +const PROPERTY_SPACE_MODE = Object.freeze({ ALL: 0, LOCAL: 1, WORLD: 2 -}; +}); + +const PROPERTY_SELECTION_VISIBILITY = Object.freeze({ + SINGLE_SELECTION: 1, + MULTIPLE_SELECTIONS: 2, + MULTI_DIFF_SELECTIONS: 4, + ANY_SELECTIONS: 7, /* SINGLE_SELECTION | MULTIPLE_SELECTIONS | MULTI_DIFF_SELECTIONS */ +}); // Multiple-selection behavior -const PROPERTY_MULTI_DISPLAY_MODE = { +const PROPERTY_MULTI_DISPLAY_MODE = Object.freeze({ DEFAULT: 0, /** * Comma separated values * Limited for properties with type "string" or "textarea" and readOnly enabled */ COMMA_SEPARATED_VALUES: 1, -}; +}); const GROUPS = [ { @@ -719,6 +726,7 @@ const GROUPS = [ type: "dynamic-multiselect", propertyUpdate: materialTargetPropertyUpdate, propertyID: "parentMaterialName", + selectionVisibility: PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION, }, { label: "Priority", @@ -1351,6 +1359,7 @@ const GROUPS = [ type: "placeholder", indentedLabel: true, propertyID: "serverScriptStatus", + selectionVisibility: PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION, }, { label: "Lifetime", @@ -1604,7 +1613,6 @@ let createAppTooltip = new CreateAppTooltip(); let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; - // mergeDeep function from https://stackoverflow.com/a/34749873 /** * Simple object check. @@ -1678,6 +1686,10 @@ function createElementFromHTML(htmlString) { return elTemplate.content.firstChild; } +function isFlagSet(value, flag) { + return (value & flag) === flag; +} + /** * GENERAL PROPERTY/GROUP FUNCTIONS */ @@ -1759,8 +1771,11 @@ function disableProperties() { } function showPropertyElement(propertyID, show) { - let elProperty = properties[propertyID].elContainer; - elProperty.style.display = show ? "" : "none"; + setPropertyVisibility(properties[propertyID], show); +} + +function setPropertyVisibility(property, visible) { + property.elContainer.style.display = visible ? null : "none"; } function resetProperties() { @@ -2001,9 +2016,9 @@ function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { }); let keys = [...uniqueKeys]; - let subPropertyDiff = {}; + let childPropertyDiff = {}; Object.entries(detailedValues).forEach(function([key, value]) { - subPropertyDiff[key] = [...new Set(value)].length > 1; + childPropertyDiff[key] = [...new Set(value)].length > 1; }); let averagePerSubProperty = {}; @@ -2014,12 +2029,12 @@ function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { return { keys, - subPropertyDiff, + childPropertyDiff, averagePerSubProperty }; } -function getDetailedSubPropertyMVPDiff(multiplePropertyValue, subPropertyName) { +function getDetailedSubPropertyMPVDiff(multiplePropertyValue, subPropertyName) { let isChecked = false; let checkedValues = multiplePropertyValue.values.map((value) => value.split(",").includes(subPropertyName)); let isMultiDiff = !checkedValues.every(value => value === checkedValues[0]); @@ -2171,7 +2186,7 @@ function createEmitNumberPropertyUpdateFunction(property) { }; } -function createEmitNumberSubPropertyUpdateFunction(property, subProperty) { +function createEmitNumberChildPropertyUpdateFunction(property, childProperty) { return function() { let propertyMultiValue = getMultiplePropertyValue(property.name); let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); @@ -2184,7 +2199,7 @@ function createEmitNumberSubPropertyUpdateFunction(property, subProperty) { let entityID = selectedEntityIDsArray[i]; let propertyObject = propertyMultiValue.values[i]; - propertyObject[subProperty] = value; + propertyObject[childProperty] = value; let updateObject = createPropertyUpdateObject(property.name, propertyObject); updateObjects.push({ @@ -2200,10 +2215,9 @@ function createEmitNumberSubPropertyUpdateFunction(property, subProperty) { // callback until drag is complete (additional update sent via dragEnd callback) let onlyUpdateEntity = properties[property.name] && properties[property.name].dragging === true; updateMultiDiffProperties(updateObjects, onlyUpdateEntity); - console.log('updateMultiDiffProperties'); } else { let propertyValue = propertyMultiValue.value; - propertyValue[subProperty] = value; + propertyValue[childProperty] = value; updateProperty(property.name, propertyValue, property.isParticleProperty); } }; @@ -2386,11 +2400,11 @@ function updateNumberMinMax(property) { /** * * @param {object} property - property update on drag - * @param {string} [subProperty] - subProperty to update on drag (e.g. enter 'x' to just update position.x) + * @param {string} [childProperty] - childProperty to update on drag (e.g. enter 'x' to just update position.x) * @returns {Function} */ -function createMultiDiffDragFunction(property, subProperty) { - return function(changedDelta) { +function createMultiDiffDragFunction(property, childProperty) { + return function(dragDelta) { let propertyMultiValue = getMultiplePropertyValue(property.name); if (!propertyMultiValue.isMultiDiffValue) { console.log("setMultiDiffDragFunction is only supposed to be called in MultiDiff mode."); @@ -2399,7 +2413,7 @@ function createMultiDiffDragFunction(property, subProperty) { let multiplier = property.data.multiplier !== undefined ? property.data.multiplier : 1; - let applyDelta = changedDelta * multiplier; + let applyDelta = dragDelta * multiplier; if (selectedEntityIDs.size !== propertyMultiValue.values.length) { console.log("selectedEntityIDs and propertyMultiValue got out of sync."); @@ -2412,9 +2426,9 @@ function createMultiDiffDragFunction(property, subProperty) { let entityID = selectedEntityIDsArray[i]; let updatedValue; - if (subProperty !== undefined) { + if (childProperty !== undefined) { let objectToUpdate = propertyMultiValue.values[i]; - objectToUpdate[subProperty] += applyDelta; + objectToUpdate[childProperty] += applyDelta; updatedValue = objectToUpdate; } else { updatedValue = propertyMultiValue.values[i] + applyDelta; @@ -2492,10 +2506,10 @@ function createRectProperty(property, elProperty) { elWidthHeightRow.appendChild(elNumberWidth.elDiv); elWidthHeightRow.appendChild(elNumberHeight.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); - elNumberWidth.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'width')); - elNumberHeight.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'height')); + elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); + elNumberWidth.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'width')); + elNumberHeight.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'height')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -2531,9 +2545,9 @@ function createVec3Property(property, elProperty) { elProperty.appendChild(elNumberY.elDiv); elProperty.appendChild(elNumberZ.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); - elNumberZ.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'z')); + elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); + elNumberZ.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'z')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -2561,8 +2575,8 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elNumberX.elDiv); elProperty.appendChild(elNumberY.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); + elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -3619,7 +3633,7 @@ function applyOutputNumberPropertyModifiers(number, propertyData) { return roundAndFixNumber(number * multiplier, propertyData); } -const isSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); +const areSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { @@ -3627,7 +3641,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { currentSelections = selections; selectedEntityIDs = new Set(selections.map(selection => selection.id)); const multipleSelections = currentSelections.length > 1; - const hasSelectedEntityChanged = !isSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); + const hasSelectedEntityChanged = !areSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); // FIXME: do we really want to save userData/materialData here instead of saving it on the blur event of the json editor? if (hasSelectedEntityChanged) { @@ -3667,9 +3681,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { // we will ignore the event. return; } + if (hasSelectedEntityChanged) { - let elServerScriptStatusOuter = document.getElementById('div-property-serverScriptStatus'); - elServerScriptStatusOuter.style.display = multipleSelections ? "none" : null; if (!multipleSelections) { resetServerScriptStatus(); } @@ -3706,6 +3719,19 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { let isMultiDiffValue = propertyMultiValue.isMultiDiffValue; let propertyValue = propertyMultiValue.value; + if (propertyData.selectionVisibility !== undefined) { + let visibility = propertyData.selectionVisibility; + let propertyVisible = true; + if (!multipleSelections) { + propertyVisible = isFlagSet(visibility, PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION); + } else if (isMultiDiffValue) { + propertyVisible = isFlagSet(visibility, PROPERTY_SELECTION_VISIBILITY.MULTI_DIFF_SELECTIONS); + } else { + propertyVisible = isFlagSet(visibility, PROPERTY_SELECTION_VISIBILITY.MULTIPLE_SELECTIONS); + } + setPropertyVisibility(property, propertyVisible); + } + const isSubProperty = propertyData.subPropertyOf !== undefined; if (propertyValue === undefined && !isMultiDiffValue && !isSubProperty) { return; @@ -3752,7 +3778,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { let propertyValue = subPropertyMultiValue.value; isMultiDiffValue = subPropertyMultiValue.isMultiDiffValue; if (isMultiDiffValue) { - let detailedSubProperty = getDetailedSubPropertyMVPDiff(subPropertyMultiValue, propertyName); + let detailedSubProperty = getDetailedSubPropertyMPVDiff(subPropertyMultiValue, propertyName); property.elInput.checked = detailedSubProperty.isChecked; property.elInput.classList.toggle('multi-diff', detailedSubProperty.isMultiDiff); } else { @@ -3774,35 +3800,30 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; } case 'number': { - if (isMultiDiffValue) { - property.elInput.value = ""; - property.elInput.classList.add('multi-diff'); - } else { - property.elInput.value = propertyValue; - property.elInput.classList.remove('multi-diff'); - } + property.elInput.value = isMultiDiffValue ? "" : propertyValue; + property.elInput.classList.toggle('multi-diff', isMultiDiffValue); break; } case 'number-draggable': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumber.setValue(detailedNumberDiff.averagePerSubProperty[0], detailedNumberDiff.subPropertyDiff[0]); + property.elNumber.setValue(detailedNumberDiff.averagePerSubProperty[0], detailedNumberDiff.childPropertyDiff[0]); break; } case 'rect': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.subPropertyDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.subPropertyDiff.y); - property.elNumberWidth.setValue(detailedNumberDiff.averagePerSubProperty.width, detailedNumberDiff.subPropertyDiff.width); - property.elNumberHeight.setValue(detailedNumberDiff.averagePerSubProperty.height, detailedNumberDiff.subPropertyDiff.height); + property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.childPropertyDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.childPropertyDiff.y); + property.elNumberWidth.setValue(detailedNumberDiff.averagePerSubProperty.width, detailedNumberDiff.childPropertyDiff.width); + property.elNumberHeight.setValue(detailedNumberDiff.averagePerSubProperty.height, detailedNumberDiff.childPropertyDiff.height); break; } case 'vec3': case 'vec2': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.subPropertyDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.subPropertyDiff.y); + property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.childPropertyDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.childPropertyDiff.y); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(detailedNumberDiff.averagePerSubProperty.z, detailedNumberDiff.subPropertyDiff.z); + property.elNumberZ.setValue(detailedNumberDiff.averagePerSubProperty.z, detailedNumberDiff.childPropertyDiff.z); } break; } @@ -3848,7 +3869,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } case 'texture': { property.elInput.value = isMultiDiffValue ? "" : propertyValue; - property.elInput.classList.toggle('multi-diff'); + property.elInput.classList.toggle('multi-diff', isMultiDiffValue); if (isMultiDiffValue) { property.elInput.setMultipleValues(); } else { From 00a024fde2bbcf7a2eb45c6655e3f8aecc60cc1a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 12 Apr 2019 20:06:32 +0200 Subject: [PATCH 09/19] fixing materialTargets, after the rebase --- scripts/system/html/js/entityProperties.js | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 6a93a84dca..42afbd5849 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1902,6 +1902,13 @@ function showGroupsForTypes(types) { }); } +function getFirstSelectedID() { + if (selectedEntityIDs.size === 0) { + return null; + } + return selectedEntityIDs.values().next().value; +} + const SUPPORTED_FALLBACK_TYPES = ['number', 'number-draggable', 'rect', 'vec3', 'vec2', 'color']; function getMultiplePropertyValue(originalPropertyName) { @@ -2992,7 +2999,7 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI */ function parentIDChanged() { - if (selectedEntityProperties.type === "Material") { + if (currentSelections.length === 1 && currentSelections[0].type === "Material") { requestMaterialTarget(); } } @@ -3478,7 +3485,10 @@ function setTextareaScrolling(element) { */ function requestMaterialTarget() { - EventBridge.emitWebEvent(JSON.stringify({ type: 'materialTargetRequest', entityID: selectedEntityProperties.id })); + EventBridge.emitWebEvent(JSON.stringify({ + type: 'materialTargetRequest', + entityID: getFirstSelectedID(), + })); } function setMaterialTargetData(materialTargetData) { @@ -3643,16 +3653,6 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { const multipleSelections = currentSelections.length > 1; const hasSelectedEntityChanged = !areSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); - // FIXME: do we really want to save userData/materialData here instead of saving it on the blur event of the json editor? - if (hasSelectedEntityChanged) { - if (editor !== null) { - saveUserData(previouslySelectedEntityIDs); - } - if (materialEditor !== null) { - saveMaterialData(previouslySelectedEntityIDs); - } - } - if (selections.length === 0) { deleteJSONEditor(); deleteJSONMaterialEditor(); @@ -4200,7 +4200,7 @@ function loaded() { } } } else if (data.type === 'materialTargetReply') { - if (data.entityID === selectedEntityProperties.id) { + if (data.entityID === getFirstSelectedID()) { setMaterialTargetData(data.materialTargetData); } } From ef7c34eaddc3bd9bd9bc5c86e81ed33ef220a091 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 15 Apr 2019 19:04:24 +0200 Subject: [PATCH 10/19] addressed more CR feedback --- scripts/system/edit.js | 10 +- scripts/system/html/js/draggableNumber.js | 3 +- scripts/system/html/js/entityProperties.js | 147 +++++++-------------- scripts/system/html/js/utils.js | 67 ++++++++++ 4 files changed, 120 insertions(+), 107 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index dbb429e86a..104648d7c4 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2365,12 +2365,10 @@ var PropertiesTool = function (opts) { if (data.properties || data.propertiesMap) { var propertiesMap = data.propertiesMap; if (propertiesMap === undefined) { - var updateEntityIDs = data.ids !== undefined ? data.ids : selectionManager.selections; - propertiesMap = []; - propertiesMap.push({ - entityIDs: updateEntityIDs, - properties: data.properties - }); + propertiesMap = [{ + entityIDs: data.ids, + properties: data.properties, + }]; } var sendListUpdate = false; diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index d76874d4d9..30e8204703 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -132,7 +132,8 @@ DraggableNumber.prototype = { } if (isNaN(newValue)) { - throw newValue + " is not a number"; + console.error("DraggableNumber.setValue() > " + newValue + " is not a number."); + return; } if (newValue !== "" && this.decimals !== undefined) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 42afbd5849..fe8a08d53d 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1613,73 +1613,6 @@ let createAppTooltip = new CreateAppTooltip(); let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; -// mergeDeep function from https://stackoverflow.com/a/34749873 -/** - * Simple object check. - * @param item - * @returns {boolean} - */ -function isObject(item) { - return (item && typeof item === 'object' && !Array.isArray(item)); -} - -/** - * Deep merge two objects. - * @param target - * @param sources - */ -function mergeDeep(target, ...sources) { - if (!sources.length) { - return target; - } - const source = sources.shift(); - - if (isObject(target) && isObject(source)) { - for (const key in source) { - if (!source.hasOwnProperty(key)) { - continue; - } - if (isObject(source[key])) { - if (!target[key]) { - Object.assign(target, { [key]: {} }); - } - mergeDeep(target[key], source[key]); - } else { - Object.assign(target, { [key]: source[key] }); - } - } - } - - return mergeDeep(target, ...sources); -} - -function deepEqual(a, b) { - if (a === b) { - return true; - } - - if (typeof(a) !== "object" || typeof(b) !== "object") { - return false; - } - - if (Object.keys(a).length !== Object.keys(b).length) { - return false; - } - - for (let property in a) { - if (!a.hasOwnProperty(property)) { - continue; - } - if (!b.hasOwnProperty(property)) { - return false; - } - if (!deepEqual(a[property], b[property])) { - return false; - } - } - return true; -} - function createElementFromHTML(htmlString) { let elTemplate = document.createElement('template'); elTemplate.innerHTML = htmlString.trim(); @@ -1909,6 +1842,15 @@ function getFirstSelectedID() { return selectedEntityIDs.values().next().value; } +/** + * Returns true when the user is currently dragging the numeric slider control of the property + * @param propertyName - name of property + * @returns {boolean} currentlyDragging + */ +function isCurrentlyDraggingProperty(propertyName) { + return properties[propertyName] && properties[propertyName].dragging === true; +} + const SUPPORTED_FALLBACK_TYPES = ['number', 'number-draggable', 'rect', 'vec3', 'vec2', 'color']; function getMultiplePropertyValue(originalPropertyName) { @@ -1993,7 +1935,12 @@ function getMultiplePropertyValue(originalPropertyName) { }; } - +/** + * Retrieve more detailed info for differing Numeric MultiplePropertyValue + * @param multiplePropertyValue - input multiplePropertyValue + * @param propertyData + * @returns {{keys: *[], propertyComponentDiff, averagePerPropertyComponent}} + */ function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { let detailedValues = {}; // Fixed numbers can't be easily averaged since they're strings, so lets keep an array of unmodified numbers @@ -2023,21 +1970,21 @@ function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { }); let keys = [...uniqueKeys]; - let childPropertyDiff = {}; + let propertyComponentDiff = {}; Object.entries(detailedValues).forEach(function([key, value]) { - childPropertyDiff[key] = [...new Set(value)].length > 1; + propertyComponentDiff[key] = [...new Set(value)].length > 1; }); - let averagePerSubProperty = {}; + let averagePerPropertyComponent = {}; Object.entries(unmodifiedValues).forEach(function([key, value]) { let average = value.reduce((a, b) => a + b) / value.length; - averagePerSubProperty[key] = applyInputNumberPropertyModifiers(average, propertyData); + averagePerPropertyComponent[key] = applyInputNumberPropertyModifiers(average, propertyData); }); return { keys, - childPropertyDiff, - averagePerSubProperty + propertyComponentDiff, + averagePerPropertyComponent, }; } @@ -2108,7 +2055,7 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) // only update the entity property value itself if in the middle of dragging // prevent undo command push, saving new property values, and property update // callback until drag is complete (additional update sent via dragEnd callback) - let onlyUpdateEntity = properties[originalPropertyName] && properties[originalPropertyName].dragging === true; + let onlyUpdateEntity = isCurrentlyDraggingProperty(originalPropertyName); updateProperties(propertyUpdate, onlyUpdateEntity); } } @@ -2193,7 +2140,7 @@ function createEmitNumberPropertyUpdateFunction(property) { }; } -function createEmitNumberChildPropertyUpdateFunction(property, childProperty) { +function createEmitNumberPropertyComponentUpdateFunction(property, propertyComponent) { return function() { let propertyMultiValue = getMultiplePropertyValue(property.name); let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); @@ -2206,7 +2153,7 @@ function createEmitNumberChildPropertyUpdateFunction(property, childProperty) { let entityID = selectedEntityIDsArray[i]; let propertyObject = propertyMultiValue.values[i]; - propertyObject[childProperty] = value; + propertyObject[propertyComponent] = value; let updateObject = createPropertyUpdateObject(property.name, propertyObject); updateObjects.push({ @@ -2220,11 +2167,11 @@ function createEmitNumberChildPropertyUpdateFunction(property, childProperty) { // only update the entity property value itself if in the middle of dragging // prevent undo command push, saving new property values, and property update // callback until drag is complete (additional update sent via dragEnd callback) - let onlyUpdateEntity = properties[property.name] && properties[property.name].dragging === true; + let onlyUpdateEntity = isCurrentlyDraggingProperty(property.name); updateMultiDiffProperties(updateObjects, onlyUpdateEntity); } else { let propertyValue = propertyMultiValue.value; - propertyValue[childProperty] = value; + propertyValue[propertyComponent] = value; updateProperty(property.name, propertyValue, property.isParticleProperty); } }; @@ -2407,10 +2354,10 @@ function updateNumberMinMax(property) { /** * * @param {object} property - property update on drag - * @param {string} [childProperty] - childProperty to update on drag (e.g. enter 'x' to just update position.x) + * @param {string} [propertyComponent] - propertyComponent to update on drag (e.g. enter 'x' to just update position.x) * @returns {Function} */ -function createMultiDiffDragFunction(property, childProperty) { +function createMultiDiffDragFunction(property, propertyComponent) { return function(dragDelta) { let propertyMultiValue = getMultiplePropertyValue(property.name); if (!propertyMultiValue.isMultiDiffValue) { @@ -2433,9 +2380,9 @@ function createMultiDiffDragFunction(property, childProperty) { let entityID = selectedEntityIDsArray[i]; let updatedValue; - if (childProperty !== undefined) { + if (propertyComponent !== undefined) { let objectToUpdate = propertyMultiValue.values[i]; - objectToUpdate[childProperty] += applyDelta; + objectToUpdate[propertyComponent] += applyDelta; updatedValue = objectToUpdate; } else { updatedValue = propertyMultiValue.values[i] + applyDelta; @@ -2513,10 +2460,10 @@ function createRectProperty(property, elProperty) { elWidthHeightRow.appendChild(elNumberWidth.elDiv); elWidthHeightRow.appendChild(elNumberHeight.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); - elNumberWidth.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'width')); - elNumberHeight.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'height')); + elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); + elNumberWidth.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'width')); + elNumberHeight.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'height')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -2552,9 +2499,9 @@ function createVec3Property(property, elProperty) { elProperty.appendChild(elNumberY.elDiv); elProperty.appendChild(elNumberZ.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); - elNumberZ.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'z')); + elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); + elNumberZ.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'z')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -2582,8 +2529,8 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elNumberX.elDiv); elProperty.appendChild(elNumberY.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); + elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -3806,24 +3753,24 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } case 'number-draggable': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumber.setValue(detailedNumberDiff.averagePerSubProperty[0], detailedNumberDiff.childPropertyDiff[0]); + property.elNumber.setValue(detailedNumberDiff.averagePerPropertyComponent[0], detailedNumberDiff.propertyComponentDiff[0]); break; } case 'rect': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.childPropertyDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.childPropertyDiff.y); - property.elNumberWidth.setValue(detailedNumberDiff.averagePerSubProperty.width, detailedNumberDiff.childPropertyDiff.width); - property.elNumberHeight.setValue(detailedNumberDiff.averagePerSubProperty.height, detailedNumberDiff.childPropertyDiff.height); + property.elNumberX.setValue(detailedNumberDiff.averagePerPropertyComponent.x, detailedNumberDiff.propertyComponentDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerPropertyComponent.y, detailedNumberDiff.propertyComponentDiff.y); + property.elNumberWidth.setValue(detailedNumberDiff.averagePerPropertyComponent.width, detailedNumberDiff.propertyComponentDiff.width); + property.elNumberHeight.setValue(detailedNumberDiff.averagePerPropertyComponent.height, detailedNumberDiff.propertyComponentDiff.height); break; } case 'vec3': case 'vec2': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.childPropertyDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.childPropertyDiff.y); + property.elNumberX.setValue(detailedNumberDiff.averagePerPropertyComponent.x, detailedNumberDiff.propertyComponentDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerPropertyComponent.y, detailedNumberDiff.propertyComponentDiff.y); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(detailedNumberDiff.averagePerSubProperty.z, detailedNumberDiff.childPropertyDiff.z); + property.elNumberZ.setValue(detailedNumberDiff.averagePerPropertyComponent.z, detailedNumberDiff.propertyComponentDiff.z); } break; } diff --git a/scripts/system/html/js/utils.js b/scripts/system/html/js/utils.js index d61b4d1762..9556856089 100644 --- a/scripts/system/html/js/utils.js +++ b/scripts/system/html/js/utils.js @@ -25,3 +25,70 @@ function disableDragDrop() { event.preventDefault(); }, false); } + +// mergeDeep function from https://stackoverflow.com/a/34749873 +/** + * Simple object check. + * @param item + * @returns {boolean} + */ +function mergeDeepIsObject(item) { + return (item && typeof item === 'object' && !Array.isArray(item)); +} + +/** + * Deep merge two objects. + * @param target + * @param sources + */ +function mergeDeep(target, ...sources) { + if (!sources.length) { + return target; + } + const source = sources.shift(); + + if (mergeDeepIsObject(target) && mergeDeepIsObject(source)) { + for (const key in source) { + if (!source.hasOwnProperty(key)) { + continue; + } + if (mergeDeepIsObject(source[key])) { + if (!target[key]) { + Object.assign(target, { [key]: {} }); + } + mergeDeep(target[key], source[key]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + + return mergeDeep(target, ...sources); +} + +function deepEqual(a, b) { + if (a === b) { + return true; + } + + if (typeof(a) !== "object" || typeof(b) !== "object") { + return false; + } + + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + + for (let property in a) { + if (!a.hasOwnProperty(property)) { + continue; + } + if (!b.hasOwnProperty(property)) { + return false; + } + if (!deepEqual(a[property], b[property])) { + return false; + } + } + return true; +} From 23918258d90c00a742e658d76b8c500d3ba0ea14 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 16 Apr 2019 10:30:09 -0700 Subject: [PATCH 11/19] fix materialJSON paths in domain baker --- libraries/baking/src/MaterialBaker.cpp | 7 ++++++- libraries/baking/src/MaterialBaker.h | 3 ++- tools/oven/src/DomainBaker.cpp | 10 +++------- tools/oven/src/DomainBaker.h | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/libraries/baking/src/MaterialBaker.cpp b/libraries/baking/src/MaterialBaker.cpp index 7fc2573d7b..79e31014fd 100644 --- a/libraries/baking/src/MaterialBaker.cpp +++ b/libraries/baking/src/MaterialBaker.cpp @@ -27,9 +27,10 @@ std::function MaterialBaker::_getNextOvenWorkerThreadOperator; static int materialNum = 0; -MaterialBaker::MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir) : +MaterialBaker::MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir, QUrl destinationPath) : _materialData(materialData), _isURL(isURL), + _destinationPath(destinationPath), _bakedOutputDir(bakedOutputDir), _textureOutputDir(bakedOutputDir + "/materialTextures/" + QString::number(materialNum++)) { @@ -177,6 +178,10 @@ void MaterialBaker::handleFinishedTextureBaker() { auto newURL = QUrl(_textureOutputDir).resolved(baker->getMetaTextureFileName()); auto relativeURL = QDir(_bakedOutputDir).relativeFilePath(newURL.toString()); + if (!_destinationPath.isEmpty()) { + relativeURL = _destinationPath.resolved(relativeURL).toDisplayString(); + } + // Replace the old texture URLs for (auto networkMaterial : _materialsNeedingRewrite.values(textureKey)) { networkMaterial->getTextureMap(baker->getMapChannel())->getTextureSource()->setUrl(relativeURL); diff --git a/libraries/baking/src/MaterialBaker.h b/libraries/baking/src/MaterialBaker.h index ab2a0a5901..7a7411142e 100644 --- a/libraries/baking/src/MaterialBaker.h +++ b/libraries/baking/src/MaterialBaker.h @@ -24,7 +24,7 @@ static const QString BAKED_MATERIAL_EXTENSION = ".baked.json"; class MaterialBaker : public Baker { Q_OBJECT public: - MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir); + MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir, QUrl destinationPath = QUrl()); QString getMaterialData() const { return _materialData; } bool isURL() const { return _isURL; } @@ -51,6 +51,7 @@ private: QString _materialData; bool _isURL; + QUrl _destinationPath; NetworkMaterialResourcePointer _materialResource; diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 50a3d212c0..7c312aff98 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -258,7 +258,7 @@ void DomainBaker::addScriptBaker(const QString& property, const QString& url, co _entitiesNeedingRewrite.insert(scriptURL, { property, jsonRef }); } -void DomainBaker::addMaterialBaker(const QString& property, const QString& data, bool isURL, const QJsonValueRef& jsonRef) { +void DomainBaker::addMaterialBaker(const QString& property, const QString& data, bool isURL, const QJsonValueRef& jsonRef, QUrl destinationPath) { // grab a clean version of the URL without a query or fragment QString materialData; if (isURL) { @@ -272,7 +272,7 @@ void DomainBaker::addMaterialBaker(const QString& property, const QString& data, // setup a baker for this material QSharedPointer materialBaker { - new MaterialBaker(data, isURL, _contentOutputPath), + new MaterialBaker(data, isURL, _contentOutputPath, destinationPath), &MaterialBaker::deleteLater }; @@ -412,13 +412,9 @@ void DomainBaker::enumerateEntities() { if (entity.contains(MATERIAL_URL_KEY)) { addMaterialBaker(MATERIAL_URL_KEY, entity[MATERIAL_URL_KEY].toString(), true, *it); } - // FIXME: Disabled for now because relative texture URLs are not supported for embedded materials in material entities - // We need to make texture URLs absolute in this particular case only, keeping in mind that FSTBaker also uses embedded materials - /* if (entity.contains(MATERIAL_DATA_KEY)) { - addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it); + addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it, _destinationPath); } - */ } } diff --git a/tools/oven/src/DomainBaker.h b/tools/oven/src/DomainBaker.h index 81f5c345cd..e8102ec7e8 100644 --- a/tools/oven/src/DomainBaker.h +++ b/tools/oven/src/DomainBaker.h @@ -76,7 +76,7 @@ private: void addModelBaker(const QString& property, const QString& url, const QJsonValueRef& jsonRef); void addTextureBaker(const QString& property, const QString& url, image::TextureUsage::Type type, const QJsonValueRef& jsonRef); void addScriptBaker(const QString& property, const QString& url, const QJsonValueRef& jsonRef); - void addMaterialBaker(const QString& property, const QString& data, bool isURL, const QJsonValueRef& jsonRef); + void addMaterialBaker(const QString& property, const QString& data, bool isURL, const QJsonValueRef& jsonRef, QUrl destinationPath = QUrl()); }; #endif // hifi_DomainBaker_h From b711ee8b8a760818d72c034191193091e2538839 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 27 Mar 2019 15:31:22 -0700 Subject: [PATCH 12/19] Fix MaterialBaker not including names in baked multi-materials --- libraries/graphics/src/graphics/Material.h | 1 + .../src/material-networking/MaterialCache.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index d24e906f98..80b247bed0 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -318,6 +318,7 @@ public: void setTextureTransforms(const Transform& transform, MaterialMappingMode mode, bool repeat); const std::string& getName() const { return _name; } + void setName(const std::string& name) { _name = name; } const std::string& getModel() const { return _model; } void setModel(const std::string& model) { _model = model; } diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp index 9eef89d5c9..5a5f4ab54b 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.cpp +++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp @@ -184,6 +184,7 @@ std::pair> NetworkMaterialResource auto nameJSON = materialJSON.value(key); if (nameJSON.isString()) { name = nameJSON.toString().toStdString(); + material->setName(name); } } else if (key == "model") { auto modelJSON = materialJSON.value(key); From 343b853b598c4d11b4ab6dbffc68b392867f42d5 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 16 Apr 2019 15:48:42 -0700 Subject: [PATCH 13/19] Fix Interface not loading some baked models correctly --- libraries/fbx/src/FBXSerializer_Mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp index c34b4678c7..3da08ade7c 100644 --- a/libraries/fbx/src/FBXSerializer_Mesh.cpp +++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp @@ -358,7 +358,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me std::vector dracoMaterialList; for (const auto& dracoChild : child.children) { if (dracoChild.name == "FBXDracoMeshVersion") { - if (!dracoChild.children.isEmpty()) { + if (!dracoChild.properties.isEmpty()) { dracoMeshNodeVersion = dracoChild.properties[0].toUInt(); } } else if (dracoChild.name == "MaterialList") { From 53f74abd4928d77f1828c7d0b2d2677aaaac67da Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 17 Apr 2019 11:33:53 -0700 Subject: [PATCH 14/19] Fix another Interface issue loading baked models --- libraries/fbx/src/FBXSerializer_Mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp index 3da08ade7c..22e6a0ddb2 100644 --- a/libraries/fbx/src/FBXSerializer_Mesh.cpp +++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp @@ -492,7 +492,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me // Figure out what material this part is if (dracoMeshNodeVersion >= 2) { // Define the materialID now - if (dracoMaterialList.size() - 1 <= materialID) { + if (materialID <= dracoMaterialList.size() - 1) { part.materialID = dracoMaterialList[materialID]; } } else { From 693cdabc2cf290478fd823913d5c283d1538d1ad Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 17 Apr 2019 13:26:19 -0700 Subject: [PATCH 15/19] Small style fix for materialID bounds check in FBXSerializer_Mesh.cpp --- libraries/fbx/src/FBXSerializer_Mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp index 22e6a0ddb2..802db4b428 100644 --- a/libraries/fbx/src/FBXSerializer_Mesh.cpp +++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp @@ -492,7 +492,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me // Figure out what material this part is if (dracoMeshNodeVersion >= 2) { // Define the materialID now - if (materialID <= dracoMaterialList.size() - 1) { + if (materialID < dracoMaterialList.size()) { part.materialID = dracoMaterialList[materialID]; } } else { From d80b40e5a57f0b281216723bb4dfd39f16723c40 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 17 Apr 2019 15:11:52 -0700 Subject: [PATCH 16/19] Fix baked FSTs unable to be rebaked by creating an FST in the original output folder --- libraries/baking/src/ModelBaker.cpp | 36 +++++++++++++++++++++++++++++ libraries/baking/src/ModelBaker.h | 1 + libraries/fbx/src/FSTReader.h | 1 + 3 files changed, 38 insertions(+) diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index e58ec00afa..00f7a38627 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -167,6 +167,10 @@ void ModelBaker::saveSourceModel() { connect(networkReply, &QNetworkReply::finished, this, &ModelBaker::handleModelNetworkReply); } + + if (_mappingURL.isEmpty()) { + outputUnbakedFST(); + } } void ModelBaker::handleModelNetworkReply() { @@ -313,6 +317,37 @@ void ModelBaker::handleFinishedMaterialBaker() { outputBakedFST(); } +void ModelBaker::outputUnbakedFST() { + // Output an unbaked FST file in the original output folder to make it easier for FSTBaker to rebake this model + // TODO: Consider a more robust method that does not depend on FSTBaker navigating to a hardcoded relative path + QString outputFSTFilename = _modelURL.fileName(); + auto extensionStart = outputFSTFilename.indexOf("."); + if (extensionStart != -1) { + outputFSTFilename.resize(extensionStart); + } + outputFSTFilename += FST_EXTENSION; + QString outputFSTURL = _originalOutputDir + "/" + outputFSTFilename; + + hifi::VariantHash outputMapping; + outputMapping[FST_VERSION_FIELD] = FST_VERSION; + outputMapping[FILENAME_FIELD] = _modelURL.fileName(); + outputMapping[COMMENT_FIELD] = "This FST file was generated by Oven for use during rebaking. It is not part of the original model. This file's existence is subject to change."; + hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping); + + QFile fstOutputFile { outputFSTURL }; + if (fstOutputFile.exists()) { + handleWarning("The file '" + outputFSTURL + "' already exists. Should that be baked instead of '" + _modelURL.toString() + "'?"); + return; + } + if (!fstOutputFile.open(QIODevice::WriteOnly)) { + handleWarning("Failed to open file '" + outputFSTURL + "' for writing. Rebaking may fail on the associated model."); + return; + } + if (fstOutputFile.write(fstOut) == -1) { + handleWarning("Failed to write to file '" + outputFSTURL + "'. Rebaking may fail on the associated model."); + } +} + void ModelBaker::outputBakedFST() { // Output FST file, copying over input mappings if available QString outputFSTFilename = !_mappingURL.isEmpty() ? _mappingURL.fileName() : _modelURL.fileName(); @@ -327,6 +362,7 @@ void ModelBaker::outputBakedFST() { outputMapping[FST_VERSION_FIELD] = FST_VERSION; outputMapping[FILENAME_FIELD] = _bakedModelURL.fileName(); outputMapping.remove(TEXDIR_FIELD); + outputMapping.remove(COMMENT_FIELD); hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping); QFile fstOutputFile { outputFSTURL }; diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h index d612a0a43a..f280481803 100644 --- a/libraries/baking/src/ModelBaker.h +++ b/libraries/baking/src/ModelBaker.h @@ -82,6 +82,7 @@ protected slots: void handleFinishedMaterialBaker(); private: + void outputUnbakedFST(); void outputBakedFST(); bool _hasBeenBaked { false }; diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index 2b13bf3078..3945fe1d8b 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -33,6 +33,7 @@ static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; static const QString JOINT_NAME_MAPPING_FIELD = "jointMap"; static const QString MATERIAL_MAPPING_FIELD = "materialMap"; +static const QString COMMENT_FIELD = "comment"; class FSTReader { public: From 386d392144c7cb5ffdbee118bc0ecf53852fc9a8 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 18 Apr 2019 19:05:40 +0200 Subject: [PATCH 17/19] The step arrows are actually clickable, they were missing multi-diff behavior before this change. < = > --- scripts/system/html/js/draggableNumber.js | 38 ++++++++++++++-------- scripts/system/html/js/entityProperties.js | 38 ++++++++++++---------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 30e8204703..3c7b74290c 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -21,7 +21,7 @@ function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) { this.initialMouseEvent = null; this.lastMouseEvent = null; this.valueChangeFunction = null; - this.multiDiffDragFunction = null; + this.multiDiffStepFunction = null; this.initialize(); } @@ -75,8 +75,8 @@ DraggableNumber.prototype = { let dragDelta = event.clientX - this.lastMouseEvent.clientX; if (dragDelta !== 0) { if (this.multiDiffModeEnabled) { - if (this.multiDiffDragFunction) { - this.multiDiffDragFunction(dragDelta * this.step); + if (this.multiDiffStepFunction) { + this.multiDiffStepFunction(dragDelta * this.step); } } else { if (dragDelta > 0) { @@ -108,20 +108,32 @@ DraggableNumber.prototype = { stepUp: function() { if (!this.isDisabled()) { - this.elInput.value = parseFloat(this.elInput.value) + this.step; - this.inputChange(); - if (this.valueChangeFunction) { - this.valueChangeFunction(); + if (this.multiDiffModeEnabled) { + if (this.multiDiffStepFunction) { + this.multiDiffStepFunction(this.step, true); + } + } else { + this.elInput.value = parseFloat(this.elInput.value) + this.step; + this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } } } }, stepDown: function() { if (!this.isDisabled()) { - this.elInput.value = parseFloat(this.elInput.value) - this.step; - this.inputChange(); - if (this.valueChangeFunction) { - this.valueChangeFunction(); + if (this.multiDiffModeEnabled) { + if (this.multiDiffStepFunction) { + this.multiDiffStepFunction(-this.step, true); + } + } else { + this.elInput.value = parseFloat(this.elInput.value) - this.step; + this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } } } }, @@ -158,8 +170,8 @@ DraggableNumber.prototype = { this.elInput.addEventListener("change", this.valueChangeFunction); }, - setMultiDiffDragFunction: function(multiDiffDragFunction) { - this.multiDiffDragFunction = multiDiffDragFunction; + setMultiDiffStepFunction: function (multiDiffStepFunction) { + this.multiDiffStepFunction = multiDiffStepFunction; }, inputChange: function() { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index fe8a08d53d..4cee3c0bc7 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -2353,21 +2353,25 @@ function updateNumberMinMax(property) { /** * - * @param {object} property - property update on drag - * @param {string} [propertyComponent] - propertyComponent to update on drag (e.g. enter 'x' to just update position.x) + * @param {object} property - property update on step + * @param {string} [propertyComponent] - propertyComponent to update on step (e.g. enter 'x' to just update position.x) * @returns {Function} */ -function createMultiDiffDragFunction(property, propertyComponent) { - return function(dragDelta) { +function createMultiDiffStepFunction(property, propertyComponent) { + return function(step, shouldAddToUndoHistory) { + if (shouldAddToUndoHistory === undefined) { + shouldAddToUndoHistory = false; + } + let propertyMultiValue = getMultiplePropertyValue(property.name); if (!propertyMultiValue.isMultiDiffValue) { - console.log("setMultiDiffDragFunction is only supposed to be called in MultiDiff mode."); + console.log("setMultiDiffStepFunction is only supposed to be called in MultiDiff mode."); return; } let multiplier = property.data.multiplier !== undefined ? property.data.multiplier : 1; - let applyDelta = dragDelta * multiplier; + let applyDelta = step * multiplier; if (selectedEntityIDs.size !== propertyMultiValue.values.length) { console.log("selectedEntityIDs and propertyMultiValue got out of sync."); @@ -2396,7 +2400,7 @@ function createMultiDiffDragFunction(property, propertyComponent) { mergeDeep(currentSelections[i].properties, propertiesUpdate); } - updateMultiDiffProperties(updateObjects, true); + updateMultiDiffProperties(updateObjects, !shouldAddToUndoHistory); } } @@ -2419,7 +2423,7 @@ function createNumberDraggableProperty(property, elProperty) { let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); elDraggableNumber.setValueChangeFunction(valueChangeFunction); - elDraggableNumber.setMultiDiffDragFunction(createMultiDiffDragFunction(property)); + elDraggableNumber.setMultiDiffStepFunction(createMultiDiffStepFunction(property)); elDraggableNumber.elInput.setAttribute("id", elementID); elProperty.appendChild(elDraggableNumber.elDiv); @@ -2465,10 +2469,10 @@ function createRectProperty(property, elProperty) { elNumberWidth.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'width')); elNumberHeight.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'height')); - elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); - elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); - elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'width')); - elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'height')); + elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); + elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); + elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'width')); + elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'height')); let elResult = []; elResult[RECT_ELEMENTS.X_NUMBER] = elNumberX; @@ -2503,9 +2507,9 @@ function createVec3Property(property, elProperty) { elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); elNumberZ.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'z')); - elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); - elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); - elNumberZ.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'z')); + elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); + elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); + elNumberZ.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'z')); let elResult = []; elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; @@ -2532,8 +2536,8 @@ function createVec2Property(property, elProperty) { elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); - elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); - elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); + elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); + elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); let elResult = []; elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; From 27f1255899a141713f87b9302b0052151728f586 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 18 Apr 2019 11:22:37 -0700 Subject: [PATCH 18/19] fix transparent textures on baked assets (cherry picked from commit b89dbf834625b59c02a025a373118b2ee1e51f36) --- .../src/model-networking/ModelCache.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 23b365dd03..75c63f99ca 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -498,6 +498,20 @@ bool Geometry::areTexturesLoaded() const { material->checkResetOpacityMap(); } + for (auto& materialMapping : _materialMapping) { + if (materialMapping.second) { + for (auto& materialPair : materialMapping.second->parsedMaterials.networkMaterials) { + if (materialPair.second) { + if (materialPair.second->isMissingTexture()) { + return false; + } + + materialPair.second->checkResetOpacityMap(); + } + } + } + } + _areTexturesLoaded = true; } return true; From 7333b02f344ecaabf80a7f5a715033711e77217f Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 19 Apr 2019 09:48:30 -0700 Subject: [PATCH 19/19] Fix model processing occurring on main thread --- .../src/material-networking/MaterialCache.h | 1 + .../src/model-networking/ModelCache.cpp | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/libraries/material-networking/src/material-networking/MaterialCache.h b/libraries/material-networking/src/material-networking/MaterialCache.h index 4894054de4..7ed0453187 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.h +++ b/libraries/material-networking/src/material-networking/MaterialCache.h @@ -108,6 +108,7 @@ private: using NetworkMaterialResourcePointer = QSharedPointer; using MaterialMapping = std::vector>; +Q_DECLARE_METATYPE(MaterialMapping) class MaterialCache : public ResourceCache { public: diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 23b365dd03..72ae224d91 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -282,8 +282,16 @@ void GeometryReader::run() { hfmModel->scripts.push_back(script.toString()); } } + + // Do processing on the model + baker::Baker modelBaker(hfmModel, _mapping.second, _mapping.first); + modelBaker.run(); + + auto processedHFMModel = modelBaker.getHFMModel(); + auto materialMapping = modelBaker.getMaterialMapping(); + QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", - Q_ARG(HFMModel::Pointer, hfmModel), Q_ARG(GeometryMappingPair, _mapping)); + Q_ARG(HFMModel::Pointer, processedHFMModel), Q_ARG(MaterialMapping, materialMapping)); } catch (const std::exception&) { auto resource = _resource.toStrongRef(); if (resource) { @@ -317,7 +325,7 @@ public: void setExtra(void* extra) override; protected: - Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, const GeometryMappingPair& mapping); + Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping); private: ModelLoader _modelLoader; @@ -340,14 +348,10 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mapping, data, _combineParts, _request->getWebMediaType())); } -void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const GeometryMappingPair& mapping) { - // Do processing on the model - baker::Baker modelBaker(hfmModel, mapping.second, mapping.first); - modelBaker.run(); - +void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) { // Assume ownership of the processed HFMModel - _hfmModel = modelBaker.getHFMModel(); - _materialMapping = modelBaker.getMaterialMapping(); + _hfmModel = hfmModel; + _materialMapping = materialMapping; // Copy materials QHash materialIDAtlas;