diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 6c6fc65be2..238c26236f 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -49,7 +49,6 @@ Item { property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif"); property int shadowHeight: 10; property bool hovered: false - property bool scrolling: false HifiConstants { id: hifi } @@ -238,31 +237,38 @@ Item { property var unhoverThunk: function () { }; Rectangle { anchors.fill: parent - visible: root.hovered && !root.scrolling + visible: root.hovered color: "transparent" border.width: 4 border.color: hifiStyleConstants.colors.primaryHighlight z: 1 } MouseArea { - anchors.fill: parent; - acceptedButtons: Qt.LeftButton; + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onContainsMouseChanged: { + // Use onContainsMouseChanged rather than onEntered and onExited because the latter aren't always + // triggered correctly - e.g., if drag rightwards from right hand side of a card to the next card + // onExited doesn't fire, in which case can end up with two cards highlighted. + if (containsMouse) { + Tablet.playSound(TabletEnums.ButtonHover); + hoverThunk(); + } else { + unhoverThunk(); + } + } + } + MouseArea { + // Separate MouseArea for click handling so that it doesn't interfere with hovering and interaction + // with containing ListView. + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: false onClicked: { Tablet.playSound(TabletEnums.ButtonClick); goFunction("hifi://" + hifiUrl); } - hoverEnabled: true; - onEntered: { - Tablet.playSound(TabletEnums.ButtonHover); - hoverThunk(); - } - onExited: unhoverThunk(); - onCanceled: unhoverThunk(); - } - MouseArea { - // This second mouse area causes onEntered to fire on the first if you scroll just a little and the cursor stays on - // the original card. I.e., the original card is re-highlighted if the cursor is on it after scrolling finishes. - anchors.fill: parent } StateImage { id: actionIcon; diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index 6928fc6f02..1e89971938 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -141,7 +141,6 @@ Column { textSizeSmall: root.textSizeSmall; stackShadowNarrowing: root.stackShadowNarrowing; shadowHeight: root.stackedCardShadowHeight; - scrolling: scroll.moving hoverThunk: function () { hovered = true; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5256ef2c6a..969a05a792 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4159,6 +4159,10 @@ void Application::focusOutEvent(QFocusEvent* event) { SpacemouseManager::getInstance().ManagerFocusOutEvent(); #endif + synthesizeKeyReleasEvents(); +} + +void Application::synthesizeKeyReleasEvents() { // synthesize events for keys currently pressed, since we may not get their release events // Because our key event handlers may manipulate _keysPressed, lets swap the keys pressed into a local copy, // clearing the existing list. @@ -4784,6 +4788,7 @@ void Application::idle() { if (_keyboardDeviceHasFocus && activeFocusItem != offscreenUi->getRootItem()) { _keyboardMouseDevice->pluginFocusOutEvent(); _keyboardDeviceHasFocus = false; + synthesizeKeyReleasEvents(); } else if (activeFocusItem == offscreenUi->getRootItem()) { _keyboardDeviceHasFocus = true; } diff --git a/interface/src/Application.h b/interface/src/Application.h index 6275feaf29..9b8aac425a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -550,6 +550,7 @@ private: void keyReleaseEvent(QKeyEvent* event); void focusOutEvent(QFocusEvent* event); + void synthesizeKeyReleasEvents(); void focusInEvent(QFocusEvent* event); void mouseMoveEvent(QMouseEvent* event); diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 0eccbacb3f..3a291bf27b 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -252,7 +252,7 @@ input.search:focus { box-shadow: 0 0 0 1px #00b4ef; } -input:disabled, textarea:disabled { +input:disabled, textarea:disabled, .draggable-number.text[disabled="disabled"] { background-color: #383838; color: #afafaf; } @@ -889,6 +889,9 @@ div.refresh input[type="button"] { border-color: #afafaf; } +.colpick { + z-index: 3; +} .colpick[disabled="disabled"] { display: none !important; } @@ -911,6 +914,79 @@ div.refresh input[type="button"] { clear: both; } +.draggable-number { + position: relative; +} +.draggable-number div { + height: 28px; + width: 92px; +} +.draggable-number.text { + display: inline-block; + color: #afafaf; + background-color: #252525; + font-family: FiraSans-SemiBold; + font-size: 15px; + margin: 0; + padding: 0 16px; + height: 28px; + width: 100%; + line-height: 2; +} +.draggable-number.text:hover { + cursor: ew-resize; +} +.draggable-number span { + position: absolute; + display: inline-block; + font-family: HiFi-Glyphs; + font-size: 20px; + z-index: 2; +} +.draggable-number span:hover { + cursor: default; +} +.draggable-number.left-arrow { + top: -5px; + right: 106px; + transform: rotate(180deg); +} +.draggable-number.right-arrow { + top: -5px; + left: 106px; +} +.draggable-number input[type=number] { + position: absolute; + right: 0; + width: 100%; +} +.draggable-number input[type=button] { + position: absolute; + top: 0; +} +.draggable-number input::-webkit-inner-spin-button { + -webkit-appearance: none; + visibility: hidden; +} +.draggable-number.fstuple { + height: 28px; + width: 124px; + left: 12px; +} +.draggable-number.fstuple + .draggable-number.fstuple { + padding-left: 28px; +} +.draggable-number.fstuple input { + right: -10px; +} +.draggable-number.fstuple .sublabel { + position: absolute; + top: 0; + left: -16px; + font-family: FiraSans-SemiBold; + font-size: 15px; +} + .row .property { width: auto; display: inline-block; @@ -927,10 +1003,10 @@ div.refresh input[type="button"] { .property.texture { display: block; } -.property.texture input{ +.property.texture input { margin: 0.4rem 0; } -.texture-image img{ +.texture-image img { padding: 0; margin: 0; width: 100%; @@ -1362,17 +1438,12 @@ input[type=button]#export { } input#property-scale-button-rescale { - margin-top: 6px; min-width: 50px; + left: 152px; } input#property-scale-button-reset { - margin-top: 6px; margin-right: 0; -} - -#property-userData-button-edit, -#property-materialData-button-clear { - margin: 6px 0 6px 0; + left: 250px; } #property-userData-static, @@ -1557,6 +1628,10 @@ input.number-slider { flex-flow: column; } +.flex-column + .flex-column { + padding-left: 50px; +} + .flex-center { align-items: center; } diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 6af8ee992a..01395368b0 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -22,6 +22,7 @@ + diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js new file mode 100644 index 0000000000..1f4bc21441 --- /dev/null +++ b/scripts/system/html/js/draggableNumber.js @@ -0,0 +1,158 @@ +// draggableNumber.js +// +// Created by David Back on 7 Nov 2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +const DELTA_X_FOCUS_THRESHOLD = 1; + +function DraggableNumber(min, max, step, decimals) { + this.min = min; + this.max = max; + this.step = step !== undefined ? step : 1; + this.decimals = decimals; + this.initialMouseEvent = null; + this.lastMouseEvent = null; + this.valueChangeFunction = null; + this.initialize(); +} + +DraggableNumber.prototype = { + mouseDown: function(event) { + if (event.target === this.elText) { + this.initialMouseEvent = event; + this.lastMouseEvent = event; + document.addEventListener("mousemove", this.onDocumentMouseMove); + document.addEventListener("mouseup", this.onDocumentMouseUp); + } + }, + + mouseUp: function(event) { + if (event.target === this.elText && this.initialMouseEvent) { + let dx = event.clientX - this.initialMouseEvent.clientX; + if (dx <= DELTA_X_FOCUS_THRESHOLD) { + this.elInput.style.visibility = "visible"; + this.elText.style.visibility = "hidden"; + } + this.initialMouseEvent = null; + } + }, + + documentMouseMove: function(event) { + if (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.stepUp(); + --dx; + } else { + this.stepDown(); + ++dx; + } + } + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } + } + this.lastMouseEvent = event; + } + }, + + documentMouseUp: function(event) { + this.lastMouseEvent = null; + document.removeEventListener("mousemove", this.onDocumentMouseMove); + document.removeEventListener("mouseup", this.onDocumentMouseUp); + }, + + stepUp: function() { + this.elInput.stepUp(); + this.inputChange(); + }, + + stepDown: function() { + this.elInput.stepDown(); + this.inputChange(); + }, + + setValue: function(newValue) { + 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; + }, + + setValueChangeFunction: function(valueChangeFunction) { + if (this.valueChangeFunction) { + this.elInput.removeEventListener("change", this.valueChangeFunction); + } + this.valueChangeFunction = valueChangeFunction.bind(this.elInput); + this.elInput.addEventListener("change", this.valueChangeFunction); + }, + + inputChange: function() { + this.setValue(this.elInput.value); + }, + + inputBlur: function() { + this.elInput.style.visibility = "hidden"; + this.elText.style.visibility = "visible"; + }, + + initialize: function() { + this.onMouseDown = this.mouseDown.bind(this); + this.onMouseUp = this.mouseUp.bind(this); + this.onDocumentMouseMove = this.documentMouseMove.bind(this); + this.onDocumentMouseUp = this.documentMouseUp.bind(this); + this.onStepUp = this.stepUp.bind(this); + this.onStepDown = this.stepDown.bind(this); + this.onInputChange = this.inputChange.bind(this); + this.onInputBlur = this.inputBlur.bind(this); + + this.elDiv = document.createElement('div'); + this.elDiv.className = "draggable-number"; + + this.elText = document.createElement('label'); + this.elText.className = "draggable-number text"; + this.elText.innerText = " "; + this.elText.style.visibility = "visible"; + this.elText.addEventListener("mousedown", this.onMouseDown); + this.elText.addEventListener("mouseup", this.onMouseUp); + + this.elLeftArrow = document.createElement('span'); + this.elRightArrow = document.createElement('span'); + this.elLeftArrow.className = 'draggable-number left-arrow'; + this.elLeftArrow.innerHTML = 'D'; + this.elLeftArrow.addEventListener("click", this.onStepDown); + this.elRightArrow.className = 'draggable-number right-arrow'; + this.elRightArrow.innerHTML = 'D'; + this.elRightArrow.addEventListener("click", this.onStepUp); + + this.elInput = document.createElement('input'); + this.elInput.className = "draggable-number input"; + this.elInput.setAttribute("type", "number"); + if (this.min !== undefined) { + this.elInput.setAttribute("min", this.min); + } + if (this.max !== undefined) { + this.elInput.setAttribute("max", this.max); + } + if (this.step !== undefined) { + this.elInput.setAttribute("step", this.step); + } + this.elInput.style.visibility = "hidden"; + this.elInput.addEventListener("change", this.onInputChange); + this.elInput.addEventListener("blur", this.onInputBlur); + + this.elText.appendChild(this.elLeftArrow); + this.elText.appendChild(this.elInput); + this.elText.appendChild(this.elRightArrow); + this.elDiv.appendChild(this.elText); + } +}; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 16c503a0bb..c49e0d88f6 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -260,7 +260,7 @@ const GROUPS = [ buttons: [ { id: "copy", label: "Copy from Skybox", className: "black", onClick: copySkyboxURLToAmbientURL } ], propertyID: "copyURLToAmbient", - showPropertyRule: { "skyboxMode": "enabled" }, + showPropertyRule: { "ambientLightMode": "enabled" }, }, { label: "Haze", @@ -315,7 +315,7 @@ const GROUPS = [ }, { label: "Background Blend", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -337,7 +337,7 @@ const GROUPS = [ }, { label: "Glare Angle", - type: "slider", + type: "number", min: 0, max: 180, step: 1, @@ -353,7 +353,7 @@ const GROUPS = [ }, { label: "Bloom Intensity", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -363,7 +363,7 @@ const GROUPS = [ }, { label: "Bloom Threshold", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -373,7 +373,7 @@ const GROUPS = [ }, { label: "Bloom Size", - type: "slider", + type: "number", min: 0, max: 2, step: 0.01, @@ -621,7 +621,7 @@ const GROUPS = [ }, { label: "Lifespan", - type: "slider", + type: "number", unit: "s", min: 0.01, max: 10, @@ -631,7 +631,7 @@ const GROUPS = [ }, { label: "Max Particles", - type: "slider", + type: "number", min: 1, max: 10000, step: 1, @@ -652,7 +652,7 @@ const GROUPS = [ properties: [ { label: "Emit Rate", - type: "slider", + type: "number", min: 1, max: 1000, step: 1, @@ -660,7 +660,7 @@ const GROUPS = [ }, { label: "Emit Speed", - type: "slider", + type: "number", min: 0, max: 5, step: 0.01, @@ -669,7 +669,7 @@ const GROUPS = [ }, { label: "Speed Spread", - type: "slider", + type: "number", min: 0, max: 5, step: 0.01, @@ -688,7 +688,7 @@ const GROUPS = [ }, { label: "Emit Radius Start", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -723,7 +723,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: 0, max: 4, step: 0.01, @@ -733,7 +733,7 @@ const GROUPS = [ }, { label: "Middle", - type: "slider", + type: "number", min: 0, max: 4, step: 0.01, @@ -742,7 +742,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: 0, max: 4, step: 0.01, @@ -754,7 +754,7 @@ const GROUPS = [ }, { label: "Size Spread", - type: "slider", + type: "number", min: 0, max: 4, step: 0.01, @@ -810,7 +810,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -820,7 +820,7 @@ const GROUPS = [ }, { label: "Middle", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -829,7 +829,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -841,7 +841,7 @@ const GROUPS = [ }, { label: "Alpha Spread", - type: "slider", + type: "number", min: 0, max: 1, step: 0.01, @@ -886,7 +886,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: -360, max: 360, step: 1, @@ -898,7 +898,7 @@ const GROUPS = [ }, { label: "Middle", - type: "slider", + type: "number", min: -360, max: 360, step: 1, @@ -909,7 +909,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: -360, max: 360, step: 1, @@ -923,7 +923,7 @@ const GROUPS = [ }, { label: "Spin Spread", - type: "slider", + type: "number", min: 0, max: 360, step: 1, @@ -950,7 +950,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: -180, max: 0, step: 1, @@ -961,7 +961,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: 0, max: 180, step: 1, @@ -978,7 +978,7 @@ const GROUPS = [ properties: [ { label: "Start", - type: "slider", + type: "number", min: 0, max: 180, step: 1, @@ -989,7 +989,7 @@ const GROUPS = [ }, { label: "Finish", - type: "slider", + type: "number", min: 0, max: 180, step: 1, @@ -1264,6 +1264,7 @@ const GROUPS = [ label: "Linear Velocity", type: "vec3", vec3Type: "xyz", + step: 0.1, decimals: 4, subLabels: [ "x", "y", "z" ], unit: "m/s", @@ -1272,6 +1273,9 @@ const GROUPS = [ { label: "Linear Damping", type: "number", + min: 0, + max: 1, + step: 0.01, decimals: 2, propertyID: "damping", }, @@ -1288,24 +1292,36 @@ const GROUPS = [ { label: "Angular Damping", type: "number", + min: 0, + max: 1, + step: 0.01, decimals: 4, propertyID: "angularDamping", }, { label: "Bounciness", type: "number", + min: 0, + max: 1, + step: 0.01, decimals: 4, propertyID: "restitution", }, { label: "Friction", type: "number", + min: 0, + max: 10, + step: 0.1, decimals: 4, propertyID: "friction", }, { label: "Density", type: "number", + min: 100, + max: 10000, + step: 1, decimals: 4, propertyID: "density", }, @@ -1314,6 +1330,8 @@ const GROUPS = [ type: "vec3", vec3Type: "xyz", subLabels: [ "x", "y", "z" ], + step: 0.1, + decimals: 4, unit: "m/s2", propertyID: "gravity", }, @@ -1374,26 +1392,16 @@ const PROPERTY_NAME_DIVISION = { }; const VECTOR_ELEMENTS = { - X_INPUT: 0, - Y_INPUT: 1, - Z_INPUT: 2, + X_NUMBER: 0, + Y_NUMBER: 1, + Z_NUMBER: 2, }; const COLOR_ELEMENTS = { COLOR_PICKER: 0, - RED_INPUT: 1, - GREEN_INPUT: 2, - BLUE_INPUT: 3, -}; - -const SLIDER_ELEMENTS = { - SLIDER: 0, - NUMBER_INPUT: 1, -}; - -const ICON_ELEMENTS = { - ICON: 0, - LABEL: 1, + RED_NUMBER: 1, + GREEN_NUMBER: 2, + BLUE_NUMBER: 3, }; const TEXTURE_ELEMENTS = { @@ -1427,17 +1435,17 @@ function getPropertyInputElement(propertyID) { switch (property.data.type) { case 'string': case 'bool': - case 'number': - case 'slider': case 'dropdown': case 'textarea': case 'texture': return property.elInput; + case 'number': + return property.elNumber.elInput; case 'vec3': case 'vec2': - return { x: property.elInputX, y: property.elInputY, z: property.elInputZ }; + return { x: property.elNumberX.elInput, y: property.elNumberY.elInput, z: property.elNumberZ.elInput }; case 'color': - return { red: property.elInputR, green: property.elInputG, blue: property.elInputB }; + return { red: property.elNumberR.elInput, green: property.elNumberG.elInput, blue: property.elNumberB.elInput }; case 'icon': return property.elLabel; default: @@ -1460,10 +1468,11 @@ function disableChildren(el, selector) { } function enableProperties() { - enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); + enableChildren(document.getElementById("properties-list"), + "input, textarea, checkbox, .dropdown dl, .color-picker , .draggable-number.text"); enableChildren(document, ".colpick"); + let elLocked = getPropertyInputElement("locked"); - if (elLocked.checked === false) { removeStaticUserData(); removeStaticMaterialData(); @@ -1471,13 +1480,14 @@ function enableProperties() { } function disableProperties() { - disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); + disableChildren(document.getElementById("properties-list"), + "input, textarea, checkbox, .dropdown dl, .color-picker, .draggable-number.text"); disableChildren(document, ".colpick"); for (let pickKey in colorPickers) { colorPickers[pickKey].colpickHide(); } + let elLocked = getPropertyInputElement("locked"); - if (elLocked.checked === true) { if ($('#property-userData-editor').css('display') === "block") { showStaticUserData(); @@ -1507,32 +1517,28 @@ function resetProperties() { property.elInput.checked = false; break; } - case 'number': - case 'slider': { + case 'number': { if (propertyData.defaultValue !== undefined) { - property.elInput.value = propertyData.defaultValue; + property.elNumber.setValue(propertyData.defaultValue); } else { - property.elInput.value = ""; - } - if (property.elSlider !== undefined) { - property.elSlider.value = property.elInput.value; + property.elNumber.setValue(""); } break; } case 'vec3': case 'vec2': { - property.elInputX.value = ""; - property.elInputY.value = ""; - if (property.elInputZ !== undefined) { - property.elInputZ.value = ""; + property.elNumberX.setValue(""); + property.elNumberY.setValue(""); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(""); } break; } case 'color': { property.elColorPicker.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - property.elInputR.value = ""; - property.elInputG.value = ""; - property.elInputB.value = ""; + property.elNumberR.setValue(""); + property.elNumberG.setValue(""); + property.elNumberB.setValue(""); break; } case 'dropdown': { @@ -1611,6 +1617,20 @@ function getPropertyValue(originalPropertyName) { return propertyValue; } +function updateVisibleSpaceModeProperties() { + for (let propertyID in properties) { + if (properties.hasOwnProperty(propertyID)) { + let property = properties[propertyID]; + let propertySpaceMode = property.spaceMode; + if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL) { + showPropertyElement(propertyID, propertySpaceMode === currentSpaceMode); + } else { + showPropertyElement(propertyID, true); + } + } + } +} + /** * PROPERTY UPDATE FUNCTIONS @@ -1797,10 +1817,12 @@ function createBoolProperty(property, elProperty) { let subPropertyOf = propertyData.subPropertyOf; if (subPropertyOf !== undefined) { elInput.addEventListener('change', function() { - updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], elInput, propertyName, property.isParticleProperty); + updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], + elInput, propertyName, property.isParticleProperty); }); } else { - elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse, property.isParticleProperty)); + elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse, + property.isParticleProperty)); } return elInput; @@ -1811,98 +1833,28 @@ function createNumberProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "number"; + elProperty.className = "draggable-number"; + + let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, + propertyData.step, propertyData.decimals); - let elInput = document.createElement('input'); - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "number"); - if (propertyData.min !== undefined) { - elInput.setAttribute("min", propertyData.min); - } - if (propertyData.max !== undefined) { - elInput.setAttribute("max", propertyData.max); - } - if (propertyData.step !== undefined) { - elInput.setAttribute("step", propertyData.step); + let defaultValue = propertyData.defaultValue; + if (defaultValue !== undefined) { + elDraggableNumber.elInput.value = defaultValue; } + + let valueChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, + property.isParticleProperty); + elDraggableNumber.setValueChangeFunction(valueChangeFunction); - let defaultValue = propertyData.defaultValue; - if (defaultValue !== undefined) { - elInput.value = defaultValue; - } - - elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty)); - - elProperty.appendChild(elInput); - + elDraggableNumber.elInput.setAttribute("id", elementID); + elProperty.appendChild(elDraggableNumber.elDiv); + if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, true); + addButtons(elDraggableNumber.elText, elementID, propertyData.buttons, false); } - return elInput; -} - -function createSliderProperty(property, elProperty) { - let propertyData = property.data; - - elProperty.className = "range"; - - let elDiv = document.createElement("div"); - elDiv.className = "slider-wrapper"; - - let elSlider = document.createElement("input"); - elSlider.setAttribute("type", "range"); - - let elInput = document.createElement("input"); - elInput.setAttribute("type", "number"); - - if (propertyData.min !== undefined) { - elInput.setAttribute("min", propertyData.min); - elSlider.setAttribute("min", propertyData.min); - } - if (propertyData.max !== undefined) { - elInput.setAttribute("max", propertyData.max); - elSlider.setAttribute("max", propertyData.max); - elSlider.setAttribute("data-max", propertyData.max); - } - if (propertyData.step !== undefined) { - elInput.setAttribute("step", propertyData.step); - elSlider.setAttribute("step", propertyData.step); - } - - elInput.onchange = function (event) { - let inputValue = event.target.value; - elSlider.value = inputValue; - if (propertyData.multiplier !== undefined) { - inputValue *= propertyData.multiplier; - } - updateProperty(property.name, inputValue, property.isParticleProperty); - }; - elSlider.oninput = function (event) { - let sliderValue = event.target.value; - if (propertyData.step === 1) { - if (sliderValue > 0) { - elInput.value = Math.floor(sliderValue); - } else { - elInput.value = Math.ceil(sliderValue); - } - } else { - elInput.value = sliderValue; - } - if (propertyData.multiplier !== undefined) { - sliderValue *= propertyData.multiplier; - } - updateProperty(property.name, sliderValue, property.isParticleProperty); - }; - - elDiv.appendChild(elSlider); - elDiv.appendChild(elInput); - elProperty.appendChild(elDiv); - - let elResult = []; - elResult[SLIDER_ELEMENTS.SLIDER] = elSlider; - elResult[SLIDER_ELEMENTS.NUMBER_INPUT] = elInput; - return elResult; + return elDraggableNumber; } function createVec3Property(property, elProperty) { @@ -1912,23 +1864,24 @@ function createVec3Property(property, elProperty) { elProperty.className = propertyData.vec3Type + " fstuple"; - let elInputX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], - propertyData.min, propertyData.max, propertyData.step); - let elInputY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], - propertyData.min, propertyData.max, propertyData.step); - let elInputZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], - propertyData.min, propertyData.max, propertyData.step); + let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); + let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); + let elNumberZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER], + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); - let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, elInputZ, - propertyData.multiplier, property.isParticleProperty); - elInputX.addEventListener('change', inputChangeFunction); - elInputY.addEventListener('change', inputChangeFunction); - elInputZ.addEventListener('change', inputChangeFunction); + let valueChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, + elNumberZ.elInput, propertyData.multiplier, + property.isParticleProperty); + elNumberX.setValueChangeFunction(valueChangeFunction); + elNumberY.setValueChangeFunction(valueChangeFunction); + elNumberZ.setValueChangeFunction(valueChangeFunction); let elResult = []; - elResult[VECTOR_ELEMENTS.X_INPUT] = elInputX; - elResult[VECTOR_ELEMENTS.Y_INPUT] = elInputY; - elResult[VECTOR_ELEMENTS.Z_INPUT] = elInputZ; + elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; + elResult[VECTOR_ELEMENTS.Y_NUMBER] = elNumberY; + elResult[VECTOR_ELEMENTS.Z_NUMBER] = elNumberZ; return elResult; } @@ -1944,19 +1897,19 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elTuple); - let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], - propertyData.min, propertyData.max, propertyData.step); - let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], - propertyData.min, propertyData.max, propertyData.step); + let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); + let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], + propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); - let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elInputX, elInputY, + let valueChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, propertyData.multiplier, property.isParticleProperty); - elInputX.addEventListener('change', inputChangeFunction); - elInputY.addEventListener('change', inputChangeFunction); + elNumberX.setValueChangeFunction(valueChangeFunction); + elNumberY.setValueChangeFunction(valueChangeFunction); let elResult = []; - elResult[VECTOR_ELEMENTS.X_INPUT] = elInputX; - elResult[VECTOR_ELEMENTS.Y_INPUT] = elInputY; + elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; + elResult[VECTOR_ELEMENTS.Y_NUMBER] = elNumberY; return elResult; } @@ -1976,15 +1929,15 @@ function createColorProperty(property, elProperty) { elProperty.appendChild(elColorPicker); elProperty.appendChild(elTuple); - let elInputR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let elInputG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let elInputB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); + let elNumberR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP); + let elNumberG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP); + let elNumberB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elInputR, elInputG, elInputB, - property.isParticleProperty); - elInputR.addEventListener('change', inputChangeFunction); - elInputG.addEventListener('change', inputChangeFunction); - elInputB.addEventListener('change', inputChangeFunction); + let valueChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elNumberR.elInput, elNumberG.elInput, + elNumberB.elInput, property.isParticleProperty); + elNumberR.setValueChangeFunction(valueChangeFunction); + elNumberG.setValueChangeFunction(valueChangeFunction); + elNumberB.setValueChangeFunction(valueChangeFunction); let colorPickerID = "#" + elementID; colorPickers[colorPickerID] = $(colorPickerID).colpick({ @@ -1997,9 +1950,9 @@ function createColorProperty(property, elProperty) { // 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({ - "r": elInputR.value, - "g": elInputG.value, - "b": elInputB.value + "r": elNumberR.elInput.value, + "g": elNumberG.elInput.value, + "b": elNumberB.elInput.value }); }, onHide: function(colpick) { @@ -2013,9 +1966,9 @@ function createColorProperty(property, elProperty) { let elResult = []; elResult[COLOR_ELEMENTS.COLOR_PICKER] = elColorPicker; - elResult[COLOR_ELEMENTS.RED_INPUT] = elInputR; - elResult[COLOR_ELEMENTS.GREEN_INPUT] = elInputG; - elResult[COLOR_ELEMENTS.BLUE_INPUT] = elInputB; + elResult[COLOR_ELEMENTS.RED_NUMBER] = elNumberR; + elResult[COLOR_ELEMENTS.GREEN_NUMBER] = elNumberG; + elResult[COLOR_ELEMENTS.BLUE_NUMBER] = elNumberB; return elResult; } @@ -2141,37 +2094,30 @@ function createButtonsProperty(property, elProperty, elLabel) { let propertyData = property.data; elProperty.className = "text"; - - let hasLabel = propertyData.label !== undefined; + if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, hasLabel); + addButtons(elProperty, elementID, propertyData.buttons, false); } return elProperty; } -function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, step) { +function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, step, decimals) { let elementID = propertyElementID + "-" + subLabel.toLowerCase(); - let elDiv = document.createElement('div'); let elLabel = document.createElement('label'); - elLabel.className = subLabel; + elLabel.className = "sublabel " + subLabel; elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1); elLabel.setAttribute("for", elementID); + elLabel.style.visibility = "visible"; - let elInput = document.createElement('input'); - elInput.className = subLabel + " number-slider"; - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "number"); - elInput.setAttribute("min", min); - elInput.setAttribute("max", max); - elInput.setAttribute("step", step); + let elDraggableNumber = new DraggableNumber(min, max, step, decimals); + elDraggableNumber.elInput.setAttribute("id", elementID); + elDraggableNumber.elDiv.className += " fstuple"; + elDraggableNumber.elText.insertBefore(elLabel, elDraggableNumber.elLeftArrow); + elTuple.appendChild(elDraggableNumber.elDiv); - elDiv.appendChild(elLabel); - elDiv.appendChild(elInput); - elTuple.appendChild(elDiv); - - return elInput; + return elDraggableNumber; } function addButtons(elProperty, propertyID, buttons, newRow) { @@ -2198,6 +2144,85 @@ function addButtons(elProperty, propertyID, buttons, newRow) { } } +function createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty) { + let property = { + data: propertyData, + elementID: propertyElementID, + name: propertyName, + elProperty: elProperty, + }; + let propertyType = propertyData.type; + + switch (propertyType) { + case 'string': { + property.elInput = createStringProperty(property, elProperty); + break; + } + case 'bool': { + property.elInput = createBoolProperty(property, elProperty); + break; + } + case 'number': { + property.elNumber = createNumberProperty(property, elProperty); + break; + } + case 'vec3': { + let elVec3 = createVec3Property(property, elProperty); + property.elNumberX = elVec3[VECTOR_ELEMENTS.X_NUMBER]; + property.elNumberY = elVec3[VECTOR_ELEMENTS.Y_NUMBER]; + property.elNumberZ = elVec3[VECTOR_ELEMENTS.Z_NUMBER]; + break; + } + case 'vec2': { + let elVec2 = createVec2Property(property, elProperty); + property.elNumberX = elVec2[VECTOR_ELEMENTS.X_NUMBER]; + property.elNumberY = elVec2[VECTOR_ELEMENTS.Y_NUMBER]; + break; + } + case 'color': { + let elColor = createColorProperty(property, elProperty); + property.elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; + property.elNumberR = elColor[COLOR_ELEMENTS.RED_NUMBER]; + property.elNumberG = elColor[COLOR_ELEMENTS.GREEN_NUMBER]; + property.elNumberB = elColor[COLOR_ELEMENTS.BLUE_NUMBER]; + break; + } + case 'dropdown': { + property.elInput = createDropdownProperty(property, propertyID, elProperty); + break; + } + case 'textarea': { + property.elInput = createTextareaProperty(property, elProperty); + break; + } + case 'icon': { + property.elSpan = createIconProperty(property, elProperty); + break; + } + case 'texture': { + let elTexture = createTextureProperty(property, elProperty); + property.elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; + property.elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; + break; + } + case 'buttons': { + property.elProperty = createButtonsProperty(property, elProperty); + break; + } + case 'placeholder': + case 'sub-header': { + break; + } + default: { + console.log("EntityProperties - Unknown property type " + + propertyType + " set to property " + propertyID); + break; + } + } + + return property; +} + /** * BUTTON CALLBACKS @@ -2717,104 +2742,6 @@ function showParentMaterialNameBox(number, elNumber, elString) { } } -function updateVisibleSpaceModeProperties() { - for (let propertyID in properties) { - if (properties.hasOwnProperty(propertyID)) { - let property = properties[propertyID]; - let propertySpaceMode = property.spaceMode; - if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL) { - showPropertyElement(propertyID, propertySpaceMode === currentSpaceMode); - } else { - showPropertyElement(propertyID, true); - } - } - } -} - -function createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty) { - let property = { - data: propertyData, - elementID: propertyElementID, - name: propertyName, - elProperty: elProperty, - }; - let propertyType = propertyData.type; - - switch (propertyType) { - case 'string': { - property.elInput = createStringProperty(property, elProperty); - break; - } - case 'bool': { - property.elInput = createBoolProperty(property, elProperty); - break; - } - case 'number': { - property.elInput = createNumberProperty(property, elProperty); - break; - } - case 'slider': { - let elSlider = createSliderProperty(property, elProperty); - property.elSlider = elSlider[SLIDER_ELEMENTS.SLIDER]; - property.elInput = elSlider[SLIDER_ELEMENTS.NUMBER_INPUT]; - break; - } - case 'vec3': { - let elVec3 = createVec3Property(property, elProperty); - property.elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT]; - property.elInputY = elVec3[VECTOR_ELEMENTS.Y_INPUT]; - property.elInputZ = elVec3[VECTOR_ELEMENTS.Z_INPUT]; - break; - } - case 'vec2': { - let elVec2 = createVec2Property(property, elProperty); - property.elInputX = elVec2[VECTOR_ELEMENTS.X_INPUT]; - property.elInputY = elVec2[VECTOR_ELEMENTS.Y_INPUT]; - break; - } - case 'color': { - let elColor = createColorProperty(property, elProperty); - property.elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; - property.elInputR = elColor[COLOR_ELEMENTS.RED_INPUT]; - property.elInputG = elColor[COLOR_ELEMENTS.GREEN_INPUT]; - property.elInputB = elColor[COLOR_ELEMENTS.BLUE_INPUT]; - break; - } - case 'dropdown': { - property.elInput = createDropdownProperty(property, propertyID, elProperty); - break; - } - case 'textarea': { - property.elInput = createTextareaProperty(property, elProperty); - break; - } - case 'icon': { - property.elSpan = createIconProperty(property, elProperty); - break; - } - case 'texture': { - let elTexture = createTextureProperty(property, elProperty); - property.elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; - property.elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; - break; - } - case 'buttons': { - property.elProperty = createButtonsProperty(property, elProperty); - break; - } - case 'placeholder': - case 'sub-header': { - break; - } - default: { - console.log("EntityProperties - Unknown property type " + - propertyType + " set to property " + propertyID); - break; - } - } - - return property; -} function loaded() { openEventBridge(function() { @@ -2896,17 +2823,16 @@ function loaded() { if (propertyData.indentedLabel || propertyData.showPropertyRule !== undefined) { let elSpan = document.createElement('span'); elSpan.className = 'indented'; - elSpan.innerText = propertyData.label; + elSpan.innerText = propertyData.label !== undefined ? propertyData.label : ""; elLabel.appendChild(elSpan); } else { - elLabel.innerText = propertyData.label; + elLabel.innerText = propertyData.label !== undefined ? propertyData.label : ""; } elContainer.appendChild(elLabel); } else { elContainer = document.getElementById(propertyData.replaceID); } - if (elLabel) { createAppTooltip.registerTooltipElement(elLabel, propertyID); } @@ -3056,7 +2982,6 @@ function loaded() { let typeProperty = properties["type"]; typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; typeProperty.elSpan.style.display = "inline-block"; - typeProperty.elLabel.innerHTML = type + " (" + data.selections.length + ")"; disableProperties(); } else { @@ -3103,7 +3028,6 @@ function loaded() { let isPropertyNotNumber = false; switch (propertyData.type) { case 'number': - case 'slider': isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; break; case 'vec3': @@ -3135,21 +3059,13 @@ function loaded() { } break; } - case 'number': - case 'slider': { + case 'number': { let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; let value = propertyValue / multiplier; if (propertyData.round !== undefined) { value = Math.round(value.round) / propertyData.round; } - if (propertyData.decimals !== undefined) { - property.elInput.value = value.toFixed(propertyData.decimals); - } else { - property.elInput.value = value; - } - if (property.elSlider !== undefined) { - property.elSlider.value = property.elInput.value; - } + property.elNumber.setValue(value); break; } case 'vec3': @@ -3164,16 +3080,16 @@ function loaded() { valueZ = Math.round(valueZ * propertyData.round) / propertyData.round; } if (propertyData.decimals !== undefined) { - property.elInputX.value = valueX.toFixed(propertyData.decimals); - property.elInputY.value = valueY.toFixed(propertyData.decimals); - if (property.elInputZ !== undefined) { - property.elInputZ.value = valueZ.toFixed(propertyData.decimals); + 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.elInputX.value = valueX; - property.elInputY.value = valueY; - if (property.elInputZ !== undefined) { - property.elInputZ.value = valueZ; + property.elNumberX.setValue(valueX); + property.elNumberY.setValue(valueY); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(valueZ); } } break; @@ -3182,9 +3098,9 @@ function loaded() { property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + propertyValue.green + "," + propertyValue.blue + ")"; - property.elInputR.value = propertyValue.red; - property.elInputG.value = propertyValue.green; - property.elInputB.value = propertyValue.blue; + property.elNumberR.setValue(propertyValue.red); + property.elNumberG.setValue(propertyValue.green); + property.elNumberB.setValue(propertyValue.blue); break; } case 'dropdown': {