diff --git a/interface/resources/images/buttonBezel.png b/interface/resources/images/buttonBezel.png new file mode 100644 index 0000000000..fe55855462 Binary files /dev/null and b/interface/resources/images/buttonBezel.png differ diff --git a/interface/resources/images/buttonBezel_highlight.png b/interface/resources/images/buttonBezel_highlight.png new file mode 100644 index 0000000000..ab0a99e4c5 Binary files /dev/null and b/interface/resources/images/buttonBezel_highlight.png differ diff --git a/interface/resources/meshes/tablet-with-home-button-small-bezel.fbx b/interface/resources/meshes/tablet-with-home-button-small-bezel.fbx index d94ce30429..9d50c36c15 100644 Binary files a/interface/resources/meshes/tablet-with-home-button-small-bezel.fbx and b/interface/resources/meshes/tablet-with-home-button-small-bezel.fbx differ diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index ab8a6c2344..be8c9f6740 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -171,6 +171,10 @@ FocusScope { } } + function textAt(index) { + return comboBox.textAt(index); + } + HifiControls.Label { id: comboBoxLabel text: root.label diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index 9d63122dbc..52d5a2eb99 100644 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -17,6 +17,10 @@ import "../controls-uit" as HifiControls SpinBox { id: spinBox + HifiConstants { + id: hifi + } + property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light property string label: "" @@ -31,8 +35,8 @@ SpinBox { property real maximumValue: 0.0 property real realValue: 0.0 - property real realFrom: 0.0 - property real realTo: 100.0 + property real realFrom: minimumValue + property real realTo: maximumValue property real realStepSize: 1.0 signal editingFinished() @@ -53,7 +57,8 @@ SpinBox { onValueChanged: realValue = value/factor stepSize: realStepSize*factor - value: realValue*factor + value: Math.round(realValue*factor) + to : realTo*factor from : realFrom*factor @@ -81,6 +86,7 @@ SpinBox { } valueFromText: function(text, locale) { + spinBox.value = 0; // Force valueChanged signal to be emitted so that validator fires. return Number.fromLocaleString(locale, text)*factor; } @@ -110,7 +116,7 @@ SpinBox { anchors.centerIn: parent text: hifi.glyphs.caratUp size: hifi.dimensions.spinnerSize - color: spinBox.down.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray + color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray } } @@ -149,26 +155,14 @@ SpinBox { visible: spinBox.labelInside != "" } -// MouseArea { -// anchors.fill: parent -// propagateComposedEvents: true -// onWheel: { -// if(spinBox.activeFocus) -// wheel.accepted = false -// else -// wheel.accepted = true -// } -// onPressed: { -// mouse.accepted = false -// } -// onReleased: { -// mouse.accepted = false -// } -// onClicked: { -// mouse.accepted = false -// } -// onDoubleClicked: { -// mouse.accepted = false -// } -// } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + onWheel: { + if (wheel.angleDelta.y > 0) + value += stepSize + else + value -= stepSize + } + } } diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml index 30e03bd02e..54270c2d06 100644 --- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml +++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml @@ -119,8 +119,8 @@ Item { colorScheme: hifi.colorSchemes.dark currentIndex: attachment ? model.indexOf(attachment.jointName) : -1 onCurrentIndexChanged: { - if (completed && attachment && currentIndex != -1 && currentText && currentText !== attachment.jointName) { - attachment.jointName = currentText; + if (completed && attachment && currentIndex != -1 && attachment.jointName !== model[currentIndex]) { + attachment.jointName = model[currentIndex]; updateAttachment(); } } diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index da8334f831..4ad37b7bc8 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -69,10 +69,15 @@ Item { id: stack initialItem: inputConfiguration property alias messageVisible: imageMessageBox.visible - property alias selectedPlugin: box.currentText Rectangle { id: inputConfiguration - anchors.fill: parent + anchors { + top: parent.top + left: parent.left + right: parent.right + } + + height: 230 HifiConstants { id: hifi } @@ -168,7 +173,7 @@ Item { text: "show all input devices" onClicked: { - inputPlugins(); + box.model = inputPlugins(); changeSource(); } } @@ -208,25 +213,28 @@ Item { anchors.leftMargin: 10 anchors.topMargin: 30 } + } + Rectangle { + id: loaderRectangle + z: -1 + color: hifi.colors.baseGray + width: parent.width + anchors.left: parent.left + anchors.right: parent.right + anchors.top: inputConfiguration.bottom + anchors.bottom: parent.bottom Loader { id: loader asynchronous: false - - width: inputConfiguration.width - anchors.left: inputConfiguration.left - anchors.right: inputConfiguration.right - anchors.top: configurationHeader.bottom - anchors.topMargin: 10 - anchors.bottom: inputConfiguration.bottom - - source: InputConfiguration.configurationLayout(box.currentText); + anchors.fill: parent + source: InputConfiguration.configurationLayout(box.textAt(box.currentIndex)); onLoaded: { if (loader.item.hasOwnProperty("pluginName")) { - if (box.currentText === "HTC Vive") { + if (box.textAt(box.currentIndex) === "HTC Vive") { loader.item.pluginName = "OpenVR"; } else { - loader.item.pluginName = box.currentText; + loader.item.pluginName = box.textAt(box.currentIndex); } } @@ -252,11 +260,12 @@ Item { function changeSource() { loader.source = ""; + var selectedDevice = box.textAt(box.currentIndex); var source = ""; - if (box.currentText == "Vive") { + if (selectedDevice == "HTC Vive") { source = InputConfiguration.configurationLayout("OpenVR"); } else { - source = InputConfiguration.configurationLayout(box.currentText); + source = InputConfiguration.configurationLayout(selectedDevice); } loader.source = source; diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 8fb49dffc0..20682372c5 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -15,1092 +15,1108 @@ import "../../controls-uit" as HifiControls import "." -Rectangle { - id: openVrConfiguration - +Flickable { + id: flick width: parent.width height: parent.height anchors.fill: parent - - property int leftMargin: 75 - property int countDown: 0 + contentHeight: 550 + flickableDirection: Flickable.VerticalFlick property string pluginName: "" - property var displayInformation: null + property var page: null; - readonly property bool feetChecked: feetBox.checked - readonly property bool hipsChecked: hipBox.checked - readonly property bool chestChecked: chestBox.checked - readonly property bool shouldersChecked: shoulderBox.checked - readonly property bool hmdHead: headBox.checked - readonly property bool headPuck: headPuckBox.checked - readonly property bool handController: handBox.checked - - readonly property bool handPuck: handPuckBox.checked - readonly property bool hmdDesktop: hmdInDesktop.checked - - property int state: buttonState.disabled - property var lastConfiguration: null - - HifiConstants { id: hifi } - - Component { id: screen; CalibratingScreen {} } - QtObject { - id: buttonState - readonly property int disabled: 0 - readonly property int apply: 1 - readonly property int applyAndCalibrate: 2 - readonly property int calibrate: 3 - - } - - MouseArea { - id: mouseArea - - anchors.fill: parent - propagateComposedEvents: true - onPressed: { - parent.forceActiveFocus() - mouse.accepted = false; - } - } - - color: hifi.colors.baseGray - - RalewayBold { - id: head - - text: "Head:" - size: 12 - - color: "white" - - anchors.left: parent.left - anchors.leftMargin: leftMargin - } - - Row { - id: headConfig - anchors.top: head.bottom - anchors.topMargin: 5 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - spacing: 10 - - HifiControls.CheckBox { - id: headBox - width: 15 - height: 15 - boxRadius: 7 - - onClicked: { - if (checked) { - headPuckBox.checked = false; - hmdInDesktop.checked = false; - } else { - checked = true; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - size: 12 - text: stack.selectedPlugin + " HMD" - color: hifi.colors.lightGrayText - } - - HifiControls.CheckBox { - id: headPuckBox - width: 15 - height: 15 - boxRadius: 7 - - onClicked: { - if (checked) { - headBox.checked = false; - hmdInDesktop.checked = false; - } else { - checked = true; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - size: 12 - text: "Tracker" - color: hifi.colors.lightGrayText - } - - HifiControls.CheckBox { - id: hmdInDesktop - width: 15 - height: 15 - boxRadius: 7 - visible: viveInDesktop.checked - - anchors.top: viveInDesktop.bottom - anchors.topMargin: 5 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - - onClicked: { - if (checked) { - headBox.checked = false; - headPuckBox.checked = false; - } else { - checked = true; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - size: 12 - visible: viveInDesktop.checked - text: "None" - color: hifi.colors.lightGrayText - } - } - - Row { - id: headOffsets - anchors.top: headConfig.bottom - anchors.topMargin: 5 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - spacing: 10 - visible: headPuckBox.checked - HifiControls.SpinBox { - id: headYOffset - decimals: 1 - width: 112 - label: "Y Offset" - suffix: " cm" - minimumValue: -10 - realStepSize: 1 - realValue: -5 - colorScheme: hifi.colorSchemes.dark - - onEditingFinished: { - sendConfigurationSettings(); - } - } - - - HifiControls.SpinBox { - id: headZOffset - width: 112 - label: "Z Offset" - minimumValue: -10 - realStepSize: 1 - decimals: 1 - suffix: " cm" - realValue: -5 - colorScheme: hifi.colorSchemes.dark - - onEditingFinished: { - sendConfigurationSettings(); - } - } - } - - RalewayBold { - id: hands - - text: "Hands:" - size: 12 - - color: "white" - - anchors.top: (headOffsets.visible ? headOffsets.bottom : headConfig.bottom) - anchors.topMargin: (headOffsets.visible ? 22 : 10) - anchors.left: parent.left - anchors.leftMargin: leftMargin - } - - Row { - id: handConfig - anchors.top: hands.bottom - anchors.topMargin: 5 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - spacing: 10 - - HifiControls.CheckBox { - id: handBox - width: 15 - height: 15 - boxRadius: 7 - - onClicked: { - if (checked) { - handPuckBox.checked = false; - } else { - checked = true; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - size: 12 - text: "Controllers" - color: hifi.colors.lightGrayText - } - - HifiControls.CheckBox { - id: handPuckBox - width: 12 - height: 15 - boxRadius: 7 - - onClicked: { - if (checked) { - handBox.checked = false; - } else { - checked = true; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - size: 12 - text: "Trackers" - color: hifi.colors.lightGrayText - } - } - - Row { - id: handOffset - visible: handPuckBox.checked - anchors.top: handConfig.bottom - anchors.topMargin: 5 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - spacing: 10 - - HifiControls.SpinBox { - id: handYOffset - decimals: 1 - width: 112 - suffix: " cm" - label: "Y Offset" - minimumValue: -10 - realStepSize: 1 - colorScheme: hifi.colorSchemes.dark - - onEditingFinished: { - sendConfigurationSettings(); - } - } - - - HifiControls.SpinBox { - id: handZOffset - width: 112 - label: "Z Offset" - suffix: " cm" - minimumValue: -10 - realStepSize: 1 - decimals: 1 - colorScheme: hifi.colorSchemes.dark - - onEditingFinished: { - sendConfigurationSettings(); - } - } - } - - RalewayBold { - id: additional - - text: "Additional Trackers" - size: 12 - - color: hifi.colors.white - - anchors.top: (handOffset.visible ? handOffset.bottom : handConfig.bottom) - anchors.topMargin: (handOffset.visible ? 22 : 10) - anchors.left: parent.left - anchors.leftMargin: leftMargin - } - - RalewayRegular { - id: info - - text: "See Recommended Tracker Placement" - color: hifi.colors.blueHighlight - size: 10 - anchors { - left: additional.right - leftMargin: 10 - verticalCenter: additional.verticalCenter - } - - Rectangle { - id: selected - color: hifi.colors.blueHighlight - - width: info.width - height: 1 - - anchors { - top: info.bottom - topMargin: 1 - left: info.left - right: info.right - } - - visible: false - } - - MouseArea { - anchors.fill: parent; - hoverEnabled: true - - onEntered: { - selected.visible = true; - } - - onExited: { - selected.visible = false; - } - onClicked: { - stack.messageVisible = true; - } - } - } - - Row { - id: feetConfig - anchors.top: additional.bottom - anchors.topMargin: 15 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - spacing: 10 - - HifiControls.CheckBox { - id: feetBox - width: 15 - height: 15 - boxRadius: 7 - - onClicked: { - if (!checked) { - shoulderBox.checked = false; - chestBox.checked = false; - hipBox.checked = false; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - size: 12 - text: "Feet" - color: hifi.colors.lightGrayText - } - } - - Row { - id: hipConfig - anchors.top: feetConfig.bottom - anchors.topMargin: 15 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - spacing: 10 - - HifiControls.CheckBox { - id: hipBox - width: 15 - height: 15 - boxRadius: 7 - - onClicked: { - if (checked) { - feetBox.checked = true; - } - - if (chestChecked) { - checked = true; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - size: 12 - text: "Hips" - color: hifi.colors.lightGrayText - } - - RalewayRegular { - size: 12 - text: "requires feet" - color: hifi.colors.lightGray - } - } - - - Row { - id: chestConfig - anchors.top: hipConfig.bottom - anchors.topMargin: 15 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - spacing: 10 - - HifiControls.CheckBox { - id: chestBox - width: 15 - height: 15 - boxRadius: 7 - - onClicked: { - if (checked) { - hipBox.checked = true; - feetBox.checked = true; - shoulderBox.checked = false; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - size: 12 - text: "Chest" - color: hifi.colors.lightGrayText - } - - RalewayRegular { - size: 12 - text: "requires hips" - color: hifi.colors.lightGray - } - } - - - Row { - id: shoulderConfig - anchors.top: chestConfig.bottom - anchors.topMargin: 15 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - spacing: 10 - - HifiControls.CheckBox { - id: shoulderBox - width: 15 - height: 15 - boxRadius: 7 - - onClicked: { - if (checked) { - hipBox.checked = true; - feetBox.checked = true; - chestBox.checked = false; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - size: 12 - text: "Shoulders" - color: hifi.colors.lightGrayText - } - - RalewayRegular { - size: 12 - text: "requires hips" - color: hifi.colors.lightGray - } - } - - Row { - id: shoulderAdditionalConfig - visible: shoulderBox.checked - anchors.top: shoulderConfig.bottom - anchors.topMargin: 5 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 20 - spacing: 10 - - HifiControls.SpinBox { - id: armCircumference - decimals: 1 - width: 160 - suffix: " cm" - label: "Arm Circumference" - minimumValue: 0 - realStepSize: 1.0 - colorScheme: hifi.colorSchemes.dark - realValue: 33.0 - - onEditingFinished: { - sendConfigurationSettings(); - } - } - - HifiControls.SpinBox { - id: shoulderWidth - width: 160 - label: "Shoulder Width" - suffix: " cm" - minimumValue: 0 - realStepSize: 1.0 - decimals: 1 - colorScheme: hifi.colorSchemes.dark - realValue: 48 - - onEditingFinished: { - sendConfigurationSettings(); - } - } - } - - Separator { - id: bottomSeperator - width: parent.width - anchors.top: shoulderAdditionalConfig.visible ? shoulderAdditionalConfig.bottom : shoulderConfig.bottom - anchors.topMargin: (shoulderAdditionalConfig.visible ? 25 : 10) - } - - Rectangle { - id: calibrationButton - property int color: hifi.buttons.blue - property int colorScheme: hifi.colorSchemes.light - property string glyph: hifi.glyphs.avatar1 - property bool enabled: false - property bool pressed: false - property bool hovered: false - property int size: 32 - property string text: "apply" - property int padding: 12 - - width: glyphButton.width + calibrationText.width + padding - height: hifi.dimensions.controlLineHeight - anchors.top: bottomSeperator.bottom - anchors.topMargin: 15 - anchors.left: parent.left - anchors.leftMargin: leftMargin - - radius: hifi.buttons.radius - - gradient: Gradient { - GradientStop { - position: 0.2 - color: { - if (!calibrationButton.enabled) { - hifi.buttons.disabledColorStart[calibrationButton.colorScheme] - } else if (calibrationButton.pressed) { - hifi.buttons.pressedColor[calibrationButton.color] - } else if (calibrationButton.hovered) { - hifi.buttons.hoveredColor[calibrationButton.color] - } else { - hifi.buttons.colorStart[calibrationButton.color] - } - } - } - - GradientStop { - position: 1.0 - color: { - if (!calibrationButton.enabled) { - hifi.buttons.disabledColorFinish[calibrationButton.colorScheme] - } else if (calibrationButton.pressed) { - hifi.buttons.pressedColor[calibrationButton.color] - } else if (calibrationButton.hovered) { - hifi.buttons.hoveredColor[calibrationButton.color] - } else { - hifi.buttons.colorFinish[calibrationButton.color] - } - } - } - } - - HiFiGlyphs { - id: glyphButton - color: enabled ? hifi.buttons.textColor[calibrationButton.color] - : hifi.buttons.disabledTextColor[calibrationButton.colorScheme] - text: calibrationButton.glyph - size: calibrationButton.size - - anchors { - top: parent.top - bottom: parent.bottom - bottomMargin: 1 - } - } - - RalewayBold { - id: calibrationText - font.capitalization: Font.AllUppercase - color: enabled ? hifi.buttons.textColor[calibrationButton.color] - : hifi.buttons.disabledTextColor[calibrationButton.colorScheme] - size: hifi.fontSizes.buttonLabel - text: calibrationButton.text - - anchors { - left: glyphButton.right - top: parent.top - topMargin: 7 - } - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - onClicked: { - if (calibrationButton.enabled) { - if (openVrConfiguration.state === buttonState.apply) { - InputConfiguration.uncalibratePlugin(pluginName); - updateCalibrationButton(); - } else { - calibrationTimer.interval = timeToCalibrate.realValue * 1000 - openVrConfiguration.countDown = timeToCalibrate.realValue; - var calibratingScreen = screen.createObject(); - stack.push(calibratingScreen); - calibratingScreen.canceled.connect(cancelCalibration); - calibratingScreen.restart.connect(restartCalibration); - calibratingScreen.start(calibrationTimer.interval, timeToCalibrate.realValue); - calibrationTimer.start(); - } - } - } - - onPressed: { - calibrationButton.pressed = true; - } - - onReleased: { - calibrationButton.pressed = false; - } - - onEntered: { - calibrationButton.hovered = true; - } - - onExited: { - calibrationButton.hovered = false; - } - } - } - - Timer { - id: calibrationTimer - repeat: false - interval: 20 - onTriggered: { - InputConfiguration.calibratePlugin(pluginName) - } - } - - Timer { - id: displayTimer - repeat: false - interval: 3000 - onTriggered: { + onPluginNameChanged: { + if (page !== null) { + page.pluginName = flick.pluginName; } } Component.onCompleted: { - InputConfiguration.calibrationStatus.connect(calibrationStatusInfo); - lastConfiguration = composeConfigurationSettings(); + page = config.createObject(flick.contentItem); } + Component { + id: config + Rectangle { + id: openVrConfiguration + property int leftMargin: 75 + property int countDown: 0 + property string pluginName: "" + property var displayInformation: null - Component.onDestruction: { - var settings = InputConfiguration.configurationSettings(pluginName); - var data = { - "num_pucks": settings["puckCount"] - } - UserActivityLogger.logAction("mocap_ui_close_dialog", data); - } + readonly property bool feetChecked: feetBox.checked + readonly property bool hipsChecked: hipBox.checked + readonly property bool chestChecked: chestBox.checked + readonly property bool shouldersChecked: shoulderBox.checked + readonly property bool hmdHead: headBox.checked + readonly property bool headPuck: headPuckBox.checked + readonly property bool handController: handBox.checked - HifiControls.SpinBox { - id: timeToCalibrate - width: 70 - anchors.top: calibrationButton.bottom - anchors.topMargin: 20 - anchors.left: parent.left - anchors.leftMargin: leftMargin + readonly property bool handPuck: handPuckBox.checked + readonly property bool hmdDesktop: hmdInDesktop.checked - minimumValue: 5 - realValue: 5 - colorScheme: hifi.colorSchemes.dark + property int state: buttonState.disabled + property var lastConfiguration: null - onEditingFinished: { - calibrationTimer.interval = realValue * 1000; - openVrConfiguration.countDown = realValue; - numberAnimation.duration = calibrationTimer.interval; - } - } + HifiConstants { id: hifi } - RalewayBold { - id: delayTextInfo - size: 10 - text: "Delay Before Calibration Starts" - color: hifi.colors.white + Component { id: screen; CalibratingScreen {} } + QtObject { + id: buttonState + readonly property int disabled: 0 + readonly property int apply: 1 + readonly property int applyAndCalibrate: 2 + readonly property int calibrate: 3 - anchors { - left: timeToCalibrate.right - leftMargin: 20 - verticalCenter: timeToCalibrate.verticalCenter - } - } - - RalewayRegular { - size: 12 - text: "sec" - color: hifi.colors.lightGray - - anchors { - left: delayTextInfo.right - leftMargin: 10 - verticalCenter: delayTextInfo.verticalCenter - } - } - - Separator { - id: advanceSeperator - width: parent.width - anchors.top: timeToCalibrate.bottom - anchors.topMargin: 10 - } - - RalewayBold { - id: advanceSettings - - text: "Advanced Settings" - size: 12 - - color: hifi.colors.white - - anchors.top: advanceSeperator.bottom - anchors.topMargin: 10 - anchors.left: parent.left - anchors.leftMargin: leftMargin - } - - - HifiControls.CheckBox { - id: viveInDesktop - width: 15 - height: 15 - boxRadius: 7 - - anchors.top: advanceSettings.bottom - anchors.topMargin: 5 - anchors.left: openVrConfiguration.left - anchors.leftMargin: leftMargin + 10 - - onClicked: { - if (!checked & hmdInDesktop.checked) { - headBox.checked = true; - headPuckBox.checked = false; - hmdInDesktop.checked = false; - } - sendConfigurationSettings(); - } - } - - RalewayBold { - id: viveDesktopText - size: 10 - text: "Use " + stack.selectedPlugin + " devices in desktop mode" - color: hifi.colors.white - - anchors { - left: viveInDesktop.right - leftMargin: 5 - verticalCenter: viveInDesktop.verticalCenter - } - } - - - NumberAnimation { - id: numberAnimation - target: openVrConfiguration - property: "countDown" - to: 0 - } - - - function logAction(action, status) { - console.log("calibrated from ui"); - var data = { - "num_pucks": status["puckCount"], - "puck_configuration": status["configuration"], - "head_puck": status["head_puck"], - "hand_puck": status["hand_pucks"] - } - UserActivityLogger.logAction(action, data); - } - - function calibrationStatusInfo(status) { - var calibrationScreen = stack.currentItem; - - if (!status["UI"]) { - calibratingScreen = screen.createObject(); - stack.push(calibratingScreen); - } - - if (status["calibrated"]) { - calibrationScreen.success(); - - if (status["UI"]) { - logAction("mocap_ui_success", status); } - } else if (!status["calibrated"]) { - calibrationScreen.failure(); + MouseArea { + id: mouseArea - if (status["UI"]) { - logAction("mocap_ui_failed", status); + anchors.fill: parent + propagateComposedEvents: true + onPressed: { + parent.forceActiveFocus() + mouse.accepted = false; + } + } + + color: hifi.colors.baseGray + + RalewayBold { + id: head + + text: "Head:" + size: 12 + + color: "white" + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + } + + Row { + id: headConfig + anchors.top: head.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + spacing: 10 + + HifiControls.CheckBox { + id: headBox + width: 15 + height: 15 + boxRadius: 7 + + onClicked: { + if (checked) { + headPuckBox.checked = false; + hmdInDesktop.checked = false; + } else { + checked = true; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + size: 12 + text: stack.selectedPlugin + " HMD" + color: hifi.colors.lightGrayText + } + + HifiControls.CheckBox { + id: headPuckBox + width: 15 + height: 15 + boxRadius: 7 + + onClicked: { + if (checked) { + headBox.checked = false; + hmdInDesktop.checked = false; + } else { + checked = true; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + size: 12 + text: "Tracker" + color: hifi.colors.lightGrayText + } + + HifiControls.CheckBox { + id: hmdInDesktop + width: 15 + height: 15 + boxRadius: 7 + visible: viveInDesktop.checked + + anchors.top: viveInDesktop.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + + onClicked: { + if (checked) { + headBox.checked = false; + headPuckBox.checked = false; + } else { + checked = true; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + size: 12 + visible: viveInDesktop.checked + text: "None" + color: hifi.colors.lightGrayText + } + } + + Row { + id: headOffsets + anchors.top: headConfig.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + spacing: 10 + visible: headPuckBox.checked + HifiControls.SpinBox { + id: headYOffset + decimals: 1 + width: 112 + label: "Y Offset" + suffix: " cm" + minimumValue: -10 + realStepSize: 1 + realValue: -5 + colorScheme: hifi.colorSchemes.dark + + onEditingFinished: { + sendConfigurationSettings(); + } + } + + + HifiControls.SpinBox { + id: headZOffset + width: 112 + label: "Z Offset" + minimumValue: -10 + realStepSize: 1 + decimals: 1 + suffix: " cm" + realValue: -5 + colorScheme: hifi.colorSchemes.dark + + onEditingFinished: { + sendConfigurationSettings(); + } + } + } + + RalewayBold { + id: hands + + text: "Hands:" + size: 12 + + color: "white" + + anchors.top: (headOffsets.visible ? headOffsets.bottom : headConfig.bottom) + anchors.topMargin: (headOffsets.visible ? 22 : 10) + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + } + + Row { + id: handConfig + anchors.top: hands.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + spacing: 10 + + HifiControls.CheckBox { + id: handBox + width: 15 + height: 15 + boxRadius: 7 + + onClicked: { + if (checked) { + handPuckBox.checked = false; + } else { + checked = true; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + size: 12 + text: "Controllers" + color: hifi.colors.lightGrayText + } + + HifiControls.CheckBox { + id: handPuckBox + width: 12 + height: 15 + boxRadius: 7 + + onClicked: { + if (checked) { + handBox.checked = false; + } else { + checked = true; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + size: 12 + text: "Trackers" + color: hifi.colors.lightGrayText + } + } + + Row { + id: handOffset + visible: handPuckBox.checked + anchors.top: handConfig.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + spacing: 10 + + HifiControls.SpinBox { + id: handYOffset + decimals: 1 + width: 112 + suffix: " cm" + label: "Y Offset" + minimumValue: -10 + realStepSize: 1 + colorScheme: hifi.colorSchemes.dark + + onEditingFinished: { + sendConfigurationSettings(); + } + } + + + HifiControls.SpinBox { + id: handZOffset + width: 112 + label: "Z Offset" + suffix: " cm" + minimumValue: -10 + realStepSize: 1 + decimals: 1 + colorScheme: hifi.colorSchemes.dark + + onEditingFinished: { + sendConfigurationSettings(); + } + } + } + + RalewayBold { + id: additional + + text: "Additional Trackers" + size: 12 + + color: hifi.colors.white + + anchors.top: (handOffset.visible ? handOffset.bottom : handConfig.bottom) + anchors.topMargin: (handOffset.visible ? 22 : 10) + anchors.left: parent.left + anchors.leftMargin: leftMargin + } + + RalewayRegular { + id: info + + text: "See Recommended Tracker Placement" + color: hifi.colors.blueHighlight + size: 10 + anchors { + left: additional.right + leftMargin: 10 + verticalCenter: additional.verticalCenter + } + + Rectangle { + id: selected + color: hifi.colors.blueHighlight + + width: info.width + height: 1 + + anchors { + top: info.bottom + topMargin: 1 + left: info.left + right: info.right + } + + visible: false + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: true + + onEntered: { + selected.visible = true; + } + + onExited: { + selected.visible = false; + } + onClicked: { + stack.messageVisible = true; + } + } + } + + Row { + id: feetConfig + anchors.top: additional.bottom + anchors.topMargin: 15 + anchors.left: parent.left + anchors.leftMargin: leftMargin + 10 + spacing: 10 + + HifiControls.CheckBox { + id: feetBox + width: 15 + height: 15 + boxRadius: 7 + + onClicked: { + if (!checked) { + shoulderBox.checked = false; + chestBox.checked = false; + hipBox.checked = false; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + size: 12 + text: "Feet" + color: hifi.colors.lightGrayText + } + } + + Row { + id: hipConfig + anchors.top: feetConfig.bottom + anchors.topMargin: 15 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + spacing: 10 + + HifiControls.CheckBox { + id: hipBox + width: 15 + height: 15 + boxRadius: 7 + + onClicked: { + if (checked) { + feetBox.checked = true; + } + + if (chestChecked) { + checked = true; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + size: 12 + text: "Hips" + color: hifi.colors.lightGrayText + } + + RalewayRegular { + size: 12 + text: "requires feet" + color: hifi.colors.lightGray + } + } + + + Row { + id: chestConfig + anchors.top: hipConfig.bottom + anchors.topMargin: 15 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + spacing: 10 + + HifiControls.CheckBox { + id: chestBox + width: 15 + height: 15 + boxRadius: 7 + + onClicked: { + if (checked) { + hipBox.checked = true; + feetBox.checked = true; + shoulderBox.checked = false; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + size: 12 + text: "Chest" + color: hifi.colors.lightGrayText + } + + RalewayRegular { + size: 12 + text: "requires hips" + color: hifi.colors.lightGray + } + } + + + Row { + id: shoulderConfig + anchors.top: chestConfig.bottom + anchors.topMargin: 15 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + spacing: 10 + + HifiControls.CheckBox { + id: shoulderBox + width: 15 + height: 15 + boxRadius: 7 + + onClicked: { + if (checked) { + hipBox.checked = true; + feetBox.checked = true; + chestBox.checked = false; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + size: 12 + text: "Shoulders" + color: hifi.colors.lightGrayText + } + + RalewayRegular { + size: 12 + text: "requires hips" + color: hifi.colors.lightGray + } + } + + Row { + id: shoulderAdditionalConfig + visible: shoulderBox.checked + anchors.top: shoulderConfig.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 20 + spacing: 10 + + HifiControls.SpinBox { + id: armCircumference + decimals: 1 + width: 160 + suffix: " cm" + label: "Arm Circumference" + minimumValue: 0 + realStepSize: 1.0 + colorScheme: hifi.colorSchemes.dark + realValue: 33.0 + + onEditingFinished: { + sendConfigurationSettings(); + } + } + + HifiControls.SpinBox { + id: shoulderWidth + width: 160 + label: "Shoulder Width" + suffix: " cm" + minimumValue: 0 + realStepSize: 1.0 + decimals: 1 + colorScheme: hifi.colorSchemes.dark + realValue: 48 + + onEditingFinished: { + sendConfigurationSettings(); + } + } + } + + Separator { + id: bottomSeperator + width: parent.width + anchors.top: shoulderAdditionalConfig.visible ? shoulderAdditionalConfig.bottom : shoulderConfig.bottom + anchors.topMargin: (shoulderAdditionalConfig.visible ? 25 : 10) + } + + Rectangle { + id: calibrationButton + property int color: hifi.buttons.blue + property int colorScheme: hifi.colorSchemes.light + property string glyph: hifi.glyphs.avatar1 + property bool enabled: false + property bool pressed: false + property bool hovered: false + property int size: 32 + property string text: "apply" + property int padding: 12 + + width: glyphButton.width + calibrationText.width + padding + height: hifi.dimensions.controlLineHeight + anchors.top: bottomSeperator.bottom + anchors.topMargin: 15 + anchors.left: parent.left + anchors.leftMargin: leftMargin + + radius: hifi.buttons.radius + + gradient: Gradient { + GradientStop { + position: 0.2 + color: { + if (!calibrationButton.enabled) { + hifi.buttons.disabledColorStart[calibrationButton.colorScheme] + } else if (calibrationButton.pressed) { + hifi.buttons.pressedColor[calibrationButton.color] + } else if (calibrationButton.hovered) { + hifi.buttons.hoveredColor[calibrationButton.color] + } else { + hifi.buttons.colorStart[calibrationButton.color] + } + } + } + + GradientStop { + position: 1.0 + color: { + if (!calibrationButton.enabled) { + hifi.buttons.disabledColorFinish[calibrationButton.colorScheme] + } else if (calibrationButton.pressed) { + hifi.buttons.pressedColor[calibrationButton.color] + } else if (calibrationButton.hovered) { + hifi.buttons.hoveredColor[calibrationButton.color] + } else { + hifi.buttons.colorFinish[calibrationButton.color] + } + } + } + } + + HiFiGlyphs { + id: glyphButton + color: enabled ? hifi.buttons.textColor[calibrationButton.color] + : hifi.buttons.disabledTextColor[calibrationButton.colorScheme] + text: calibrationButton.glyph + size: calibrationButton.size + + anchors { + top: parent.top + bottom: parent.bottom + bottomMargin: 1 + } + } + + RalewayBold { + id: calibrationText + font.capitalization: Font.AllUppercase + color: enabled ? hifi.buttons.textColor[calibrationButton.color] + : hifi.buttons.disabledTextColor[calibrationButton.colorScheme] + size: hifi.fontSizes.buttonLabel + text: calibrationButton.text + + anchors { + left: glyphButton.right + top: parent.top + topMargin: 7 + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onClicked: { + if (calibrationButton.enabled) { + if (openVrConfiguration.state === buttonState.apply) { + InputConfiguration.uncalibratePlugin(openVrConfiguration.pluginName); + updateCalibrationButton(); + } else { + calibrationTimer.interval = timeToCalibrate.realValue * 1000 + openVrConfiguration.countDown = timeToCalibrate.realValue; + var calibratingScreen = screen.createObject(); + stack.push(calibratingScreen); + calibratingScreen.canceled.connect(cancelCalibration); + calibratingScreen.restart.connect(restartCalibration); + calibratingScreen.start(calibrationTimer.interval, timeToCalibrate.realValue); + calibrationTimer.start(); + } + } + } + + onPressed: { + calibrationButton.pressed = true; + } + + onReleased: { + calibrationButton.pressed = false; + } + + onEntered: { + calibrationButton.hovered = true; + } + + onExited: { + calibrationButton.hovered = false; + } + } + } + + Timer { + id: calibrationTimer + repeat: false + interval: 20 + onTriggered: { + InputConfiguration.calibratePlugin(openVrConfiguration.pluginName) + } + } + + Timer { + id: displayTimer + repeat: false + interval: 3000 + onTriggered: { + } + } + + Component.onCompleted: { + InputConfiguration.calibrationStatus.connect(calibrationStatusInfo); + lastConfiguration = composeConfigurationSettings(); + } + + Component.onDestruction: { + var settings = InputConfiguration.configurationSettings(openVrConfiguration.pluginName); + var data = { + "num_pucks": settings["puckCount"] + } + UserActivityLogger.logAction("mocap_ui_close_dialog", data); + } + + HifiControls.SpinBox { + id: timeToCalibrate + width: 70 + anchors.top: calibrationButton.bottom + anchors.topMargin: 20 + anchors.left: parent.left + anchors.leftMargin: leftMargin + + minimumValue: 5 + realValue: 5 + colorScheme: hifi.colorSchemes.dark + + onEditingFinished: { + calibrationTimer.interval = realValue * 1000; + openVrConfiguration.countDown = realValue; + numberAnimation.duration = calibrationTimer.interval; + } + } + + RalewayBold { + id: delayTextInfo + size: 10 + text: "Delay Before Calibration Starts" + color: hifi.colors.white + + anchors { + left: timeToCalibrate.right + leftMargin: 20 + verticalCenter: timeToCalibrate.verticalCenter + } + } + + RalewayRegular { + size: 12 + text: "sec" + color: hifi.colors.lightGray + + anchors { + left: delayTextInfo.right + leftMargin: 10 + verticalCenter: delayTextInfo.verticalCenter + } + } + + Separator { + id: advanceSeperator + width: parent.width + anchors.top: timeToCalibrate.bottom + anchors.topMargin: 10 + } + + RalewayBold { + id: advanceSettings + + text: "Advanced Settings" + size: 12 + + color: hifi.colors.white + + anchors.top: advanceSeperator.bottom + anchors.topMargin: 10 + anchors.left: parent.left + anchors.leftMargin: leftMargin + } + + + HifiControls.CheckBox { + id: viveInDesktop + width: 15 + height: 15 + boxRadius: 7 + + anchors.top: advanceSettings.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + + onClicked: { + if (!checked & hmdInDesktop.checked) { + headBox.checked = true; + headPuckBox.checked = false; + hmdInDesktop.checked = false; + } + sendConfigurationSettings(); + } + } + + RalewayBold { + id: viveDesktopText + size: 10 + text: "Use " + stack.selectedPlugin + " devices in desktop mode" + color: hifi.colors.white + + anchors { + left: viveInDesktop.right + leftMargin: 5 + verticalCenter: viveInDesktop.verticalCenter + } + } + + + NumberAnimation { + id: numberAnimation + target: openVrConfiguration + property: "countDown" + to: 0 + } + + + function logAction(action, status) { + var data = { + "num_pucks": status["puckCount"], + "puck_configuration": status["configuration"], + "head_puck": status["head_puck"], + "hand_puck": status["hand_pucks"] + } + UserActivityLogger.logAction(action, data); + } + + function calibrationStatusInfo(status) { + var calibrationScreen = stack.currentItem; + + if (!status["UI"]) { + calibratingScreen = screen.createObject(); + stack.push(calibratingScreen); + } + + if (status["calibrated"]) { + calibrationScreen.success(); + + if (status["UI"]) { + logAction("mocap_ui_success", status); + } + + } else if (!status["calibrated"]) { + calibrationScreen.failure(); + + if (status["UI"]) { + logAction("mocap_ui_failed", status); + } + } + updateCalibrationButton(); + } + + + function trackersForConfiguration() { + var pucksNeeded = 0; + + if (headPuckBox.checked) { + pucksNeeded++; + } + + if (feetBox.checked) { + pucksNeeded++; + } + + if (hipBox.checked) { + pucksNeeded++; + } + + if (chestBox.checked) { + pucksNeeded++; + } + + if (shoulderBox.checked) { + pucksNeeded++; + } + + return pucksNeeded; + } + + function cancelCalibration() { + calibrationTimer.stop(); + } + + function restartCalibration() { + calibrationTimer.restart(); + } + + function displayConfiguration() { + var settings = InputConfiguration.configurationSettings(openVrConfiguration.pluginName); + var configurationType = settings["trackerConfiguration"]; + displayTrackerConfiguration(configurationType); + + + var HmdHead = settings["HMDHead"]; + var viveController = settings["handController"]; + var desktopMode = settings["desktopMode"]; + var hmdDesktopPosition = settings["hmdDesktopTracking"]; + + armCircumference.realValue = settings.armCircumference; + shoulderWidth.realValue = settings.shoulderWidth; + + if (HmdHead) { + headBox.checked = true; + headPuckBox.checked = false; + } else { + headPuckBox.checked = true; + headBox.checked = false; + } + + if (viveController) { + handBox.checked = true; + handPuckBox.checked = false; + } else { + handPuckBox.checked = true; + handBox.checked = false; + } + + viveInDesktop.checked = desktopMode; + hmdInDesktop.checked = hmdDesktopPosition; + + initializeButtonState(); + updateCalibrationText(); + + var data = { + "num_pucks": settings["puckCount"] + }; + + UserActivityLogger.logAction("mocap_ui_open_dialog", data); + } + + function displayTrackerConfiguration(type) { + if (type === "Feet") { + feetBox.checked = true; + } else if (type === "FeetAndHips") { + feetBox.checked = true; + hipBox.checked = true; + } else if (type === "FeetHipsChest") { + feetBox.checked = true; + hipBox.checked = true; + chestBox.checked = true; + } else if (type === "FeetHipsAndShoulders") { + feetBox.checked = true; + hipBox.checked = true; + shoulderBox.checked = true; + } else if (type === "FeetHipsChestAndShoulders") { + feetBox.checked = true; + hipBox.checked = true; + chestBox.checked = true; + shoulderBox.checked = true; + } + } + + function updateButtonState() { + var settings = composeConfigurationSettings(); + var bodySetting = settings["bodyConfiguration"]; + var headSetting = settings["headConfiguration"]; + var headOverride = headSetting["override"]; + var handSetting = settings["handConfiguration"]; + var handOverride = handSetting["override"]; + + var settingsChanged = false; + + if (lastConfiguration["bodyConfiguration"] !== bodySetting) { + settingsChanged = true; + } + + var lastHead = lastConfiguration["headConfiguration"]; + if (lastHead["override"] !== headOverride) { + settingsChanged = true; + } + + var lastHand = lastConfiguration["handConfiguration"]; + if (lastHand["override"] !== handOverride) { + settingsChanged = true; + } + + if (settingsChanged) { + if ((!handOverride) && (!headOverride) && (bodySetting === "None")) { + state = buttonState.apply; + } else { + state = buttonState.applyAndCalibrate; + } + } else { + if (state == buttonState.apply) { + state = buttonState.disabled; + } else if (state == buttonState.applyAndCalibrate) { + state = buttonState.calibrate; + } + } + + lastConfiguration = settings; + } + + function initializeButtonState() { + var settings = composeConfigurationSettings(); + var bodySetting = settings["bodyConfiguration"]; + var headSetting = settings["headConfiguration"]; + var headOverride = headSetting["override"]; + var handSetting = settings["handConfiguration"]; + var handOverride = handSetting["override"]; + + + if ((!handOverride) && (!headOverride) && (bodySetting === "None")) { + state = buttonState.disabled; + } else { + state = buttonState.calibrate; + } + } + + function updateCalibrationButton() { + updateButtonState(); + updateCalibrationText(); + } + + function updateCalibrationText() { + if (buttonState.disabled == state) { + calibrationButton.enabled = false; + calibrationButton.text = "Apply"; + } else if (buttonState.apply == state) { + calibrationButton.enabled = true; + calibrationButton.text = "Apply"; + } else if (buttonState.applyAndCalibrate == state) { + calibrationButton.enabled = true; + calibrationButton.text = "Apply And Calibrate"; + } else if (buttonState.calibrate == state) { + calibrationButton.enabled = true; + calibrationButton.text = "Calibrate"; + } + } + + function composeConfigurationSettings() { + var trackerConfiguration = ""; + var overrideHead = false; + var overrideHandController = false; + + if (shouldersChecked && chestChecked) { + trackerConfiguration = "FeetHipsChestAndShoulders"; + } else if (shouldersChecked) { + trackerConfiguration = "FeetHipsAndShoulders"; + } else if (chestChecked) { + trackerConfiguration = "FeetHipsAndChest"; + } else if (hipsChecked) { + trackerConfiguration = "FeetAndHips"; + } else if (feetChecked) { + trackerConfiguration = "Feet"; + } else { + trackerConfiguration = "None"; + } + + if (headPuck) { + overrideHead = true; + } else if (hmdHead) { + overrideHead = false; + } + + if (handController) { + overrideHandController = false; + } else if (handPuck) { + overrideHandController = true; + } + + var headObject = { + "override": overrideHead, + "Y": headYOffset.realValue, + "Z": headZOffset.realValue + } + + var handObject = { + "override": overrideHandController, + "Y": handYOffset.realValue, + "Z": handZOffset.realValue + } + + var settingsObject = { + "bodyConfiguration": trackerConfiguration, + "headConfiguration": headObject, + "handConfiguration": handObject, + "armCircumference": armCircumference.realValue, + "shoulderWidth": shoulderWidth.realValue, + "desktopMode": viveInDesktop.checked, + "hmdDesktopTracking": hmdInDesktop.checked + } + + return settingsObject; + } + + function sendConfigurationSettings() { + var settings = composeConfigurationSettings(); + InputConfiguration.setConfigurationSettings(settings, openVrConfiguration.pluginName); + updateCalibrationButton(); } } - updateCalibrationButton(); - } - - - function trackersForConfiguration() { - var pucksNeeded = 0; - - if (headPuckBox.checked) { - pucksNeeded++; - } - - if (feetBox.checked) { - pucksNeeded++; - } - - if (hipBox.checked) { - pucksNeeded++; - } - - if (chestBox.checked) { - pucksNeeded++; - } - - if (shoulderBox.checked) { - pucksNeeded++; - } - - return pucksNeeded; - } - - function cancelCalibration() { - calibrationTimer.stop(); - } - - function restartCalibration() { - calibrationTimer.restart(); - } - - function displayConfiguration() { - var settings = InputConfiguration.configurationSettings(pluginName); - var configurationType = settings["trackerConfiguration"]; - displayTrackerConfiguration(configurationType); - - - var HmdHead = settings["HMDHead"]; - var viveController = settings["handController"]; - var desktopMode = settings["desktopMode"]; - var hmdDesktopPosition = settings["hmdDesktopTracking"]; - - armCircumference.realValue = settings.armCircumference; - shoulderWidth.realValue = settings.shoulderWidth; - - if (HmdHead) { - headBox.checked = true; - headPuckBox.checked = false; - } else { - headPuckBox.checked = true; - headBox.checked = false; - } - - if (viveController) { - handBox.checked = true; - handPuckBox.checked = false; - } else { - handPuckBox.checked = true; - handBox.checked = false; - } - - viveInDesktop.checked = desktopMode; - hmdInDesktop.checked = hmdDesktopPosition; - - initializeButtonState(); - updateCalibrationText(); - - var data = { - "num_pucks": settings["puckCount"] - }; - - UserActivityLogger.logAction("mocap_ui_open_dialog", data); - } - - function displayTrackerConfiguration(type) { - if (type === "Feet") { - feetBox.checked = true; - } else if (type === "FeetAndHips") { - feetBox.checked = true; - hipBox.checked = true; - } else if (type === "FeetHipsChest") { - feetBox.checked = true; - hipBox.checked = true; - chestBox.checked = true; - } else if (type === "FeetHipsAndShoulders") { - feetBox.checked = true; - hipBox.checked = true; - shoulderBox.checked = true; - } else if (type === "FeetHipsChestAndShoulders") { - feetBox.checked = true; - hipBox.checked = true; - chestBox.checked = true; - shoulderBox.checked = true; - } - } - - function updateButtonState() { - var settings = composeConfigurationSettings(); - var bodySetting = settings["bodyConfiguration"]; - var headSetting = settings["headConfiguration"]; - var headOverride = headSetting["override"]; - var handSetting = settings["handConfiguration"]; - var handOverride = handSetting["override"]; - - var settingsChanged = false; - - if (lastConfiguration["bodyConfiguration"] !== bodySetting) { - settingsChanged = true; - } - - var lastHead = lastConfiguration["headConfiguration"]; - if (lastHead["override"] !== headOverride) { - settingsChanged = true; - } - - var lastHand = lastConfiguration["handConfiguration"]; - if (lastHand["override"] !== handOverride) { - settingsChanged = true; - } - - if (settingsChanged) { - if ((!handOverride) && (!headOverride) && (bodySetting === "None")) { - state = buttonState.apply; - } else { - state = buttonState.applyAndCalibrate; - } - } else { - if (state == buttonState.apply) { - state = buttonState.disabled; - } else if (state == buttonState.applyAndCalibrate) { - state = buttonState.calibrate; - } - } - - lastConfiguration = settings; - } - - function initializeButtonState() { - var settings = composeConfigurationSettings(); - var bodySetting = settings["bodyConfiguration"]; - var headSetting = settings["headConfiguration"]; - var headOverride = headSetting["override"]; - var handSetting = settings["handConfiguration"]; - var handOverride = handSetting["override"]; - - - if ((!handOverride) && (!headOverride) && (bodySetting === "None")) { - state = buttonState.disabled; - } else { - state = buttonState.calibrate; - } - } - - function updateCalibrationButton() { - updateButtonState(); - updateCalibrationText(); - } - - function updateCalibrationText() { - if (buttonState.disabled == state) { - calibrationButton.enabled = false; - calibrationButton.text = "Apply"; - } else if (buttonState.apply == state) { - calibrationButton.enabled = true; - calibrationButton.text = "Apply"; - } else if (buttonState.applyAndCalibrate == state) { - calibrationButton.enabled = true; - calibrationButton.text = "Apply And Calibrate"; - } else if (buttonState.calibrate == state) { - calibrationButton.enabled = true; - calibrationButton.text = "Calibrate"; - } - } - - function composeConfigurationSettings() { - var trackerConfiguration = ""; - var overrideHead = false; - var overrideHandController = false; - - if (shouldersChecked && chestChecked) { - trackerConfiguration = "FeetHipsChestAndShoulders"; - } else if (shouldersChecked) { - trackerConfiguration = "FeetHipsAndShoulders"; - } else if (chestChecked) { - trackerConfiguration = "FeetHipsAndChest"; - } else if (hipsChecked) { - trackerConfiguration = "FeetAndHips"; - } else if (feetChecked) { - trackerConfiguration = "Feet"; - } else { - trackerConfiguration = "None"; - } - - if (headPuck) { - overrideHead = true; - } else if (hmdHead) { - overrideHead = false; - } - - if (handController) { - overrideHandController = false; - } else if (handPuck) { - overrideHandController = true; - } - - var headObject = { - "override": overrideHead, - "Y": headYOffset.realValue, - "Z": headZOffset.realValue - } - - var handObject = { - "override": overrideHandController, - "Y": handYOffset.realValue, - "Z": handZOffset.realValue - } - - var settingsObject = { - "bodyConfiguration": trackerConfiguration, - "headConfiguration": headObject, - "handConfiguration": handObject, - "armCircumference": armCircumference.realValue, - "shoulderWidth": shoulderWidth.realValue, - "desktopMode": viveInDesktop.checked, - "hmdDesktopTracking": hmdInDesktop.checked - } - - return settingsObject; - } - - function sendConfigurationSettings() { - var settings = composeConfigurationSettings(); - InputConfiguration.setConfigurationSettings(settings, pluginName); - updateCalibrationButton(); } } diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index d2a272851f..d4dba2f0f5 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -56,7 +56,8 @@ class QScriptEngine; * @property {Uuid} tabletID - The UUID of the tablet body model overlay. * @property {Uuid} tabletScreenID - The UUID of the tablet's screen overlay. * @property {Uuid} homeButtonID - The UUID of the tablet's "home" button overlay. - * @property {Uuid} homeButtonHighlightID - The UUID of the tablet's "home" button highlight overlay. + * @property {Uuid} homeButtonHighlightMaterialID - The UUID of the material entity used to highlight tablet button + * @property {Uuid} homeButtonUnhighlightMaterialID - The UUID of the material entity use to unhighlight the entity */ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency { Q_OBJECT @@ -67,8 +68,9 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen Q_PROPERTY(bool tabletContextualMode READ getTabletContextualMode) Q_PROPERTY(QUuid tabletID READ getCurrentTabletFrameID WRITE setCurrentTabletFrameID) Q_PROPERTY(QUuid homeButtonID READ getCurrentHomeButtonID WRITE setCurrentHomeButtonID) - Q_PROPERTY(QUuid homeButtonHighlightID READ getCurrentHomeButtonHightlightID WRITE setCurrentHomeButtonHightlightID) Q_PROPERTY(QUuid tabletScreenID READ getCurrentTabletScreenID WRITE setCurrentTabletScreenID) + Q_PROPERTY(QUuid homeButtonHighlightMaterialID READ getCurrentHomeButtonHighlightMaterialID WRITE setCurrentHomeButtonHighlightMaterialID) + Q_PROPERTY(QUuid homeButtonUnhighlightMaterialID READ getCurrentHomeButtonUnhighlightMaterialID WRITE setCurrentHomeButtonUnhighlightMaterialID) public: @@ -372,20 +374,24 @@ public: void setCurrentHomeButtonID(QUuid homeButtonID) { _homeButtonID = homeButtonID; } QUuid getCurrentHomeButtonID() const { return _homeButtonID; } - void setCurrentHomeButtonHightlightID(QUuid homeButtonHightlightID) { _homeButtonHightlightID = homeButtonHightlightID; } - QUuid getCurrentHomeButtonHightlightID() const { return _homeButtonHightlightID; } - void setCurrentTabletScreenID(QUuid tabletID) { _tabletScreenID = tabletID; } QUuid getCurrentTabletScreenID() const { return _tabletScreenID; } + void setCurrentHomeButtonHighlightMaterialID(QUuid homeButtonHighlightMaterialID) { _homeButtonHighlightMaterialID = homeButtonHighlightMaterialID; } + QUuid getCurrentHomeButtonHighlightMaterialID() { return _homeButtonHighlightMaterialID; } + + void setCurrentHomeButtonUnhighlightMaterialID(QUuid homeButtonUnhighlightMaterialID) { _homeButtonUnhighlightMaterialID = homeButtonUnhighlightMaterialID; } + QUuid getCurrentHomeButtonUnhighlightMaterialID() { return _homeButtonUnhighlightMaterialID; } + private: bool _showTablet { false }; bool _tabletContextualMode { false }; QUuid _tabletUIID; // this is the entityID of the tablet frame QUuid _tabletScreenID; // this is the overlayID which is part of (a child of) the tablet-ui. QUuid _homeButtonID; - QUuid _homeButtonHightlightID; QUuid _tabletEntityID; + QUuid _homeButtonHighlightMaterialID; + QUuid _homeButtonUnhighlightMaterialID; // Get the position of the HMD glm::vec3 getPosition() const; diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h index 8b5c12e3dc..83aec63ee2 100644 --- a/interface/src/scripting/SelectionScriptingInterface.h +++ b/interface/src/scripting/SelectionScriptingInterface.h @@ -182,7 +182,7 @@ public: /**jsdoc * Get the list of avatars, entities, and overlays stored in a selection list. - * @function Selection.getList + * @function Selection.getSelectedItemsList * @param {string} listName - The name of the selection list. * @returns {Selection.SelectedItemsList} The content of a selection list. If the list name doesn't exist, the function * returns an empty object with no properties. @@ -257,7 +257,7 @@ public: void onSelectedItemsListChanged(const QString& listName); signals: - /**jsoc + /**jsdoc * Triggered when a list's content changes. * @function Selection.selectedItemsListChanged * @param {string} listName - The name of the selection list that changed. diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index f59b513bd5..551b352952 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -283,7 +283,7 @@ QVariant Base3DOverlay::getProperty(const QString& property) { } bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal) { + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { return false; } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 2a63a6cb67..7c5f551e6a 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -69,11 +69,11 @@ public: virtual QVariant getProperty(const QString& property) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal); + BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false); virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo) { - return findRayIntersection(origin, direction, distance, face, surfaceNormal); + float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) { + return findRayIntersection(origin, direction, distance, face, surfaceNormal, precisionPicking); } virtual SpatialParentTree* getParentTree() const override; diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 33f40f7c63..ef89213d68 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -521,7 +521,7 @@ QVariant Circle3DOverlay::getProperty(const QString& property) { } bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal) { + BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { // Scale the dimensions by the diameter glm::vec2 dimensions = getOuterRadius() * 2.0f * getDimensions(); diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index ef491b7f46..0dc0f8b138 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -55,7 +55,7 @@ public: void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal) override; + BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; virtual Circle3DOverlay* createClone() const override; diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h index 5a67b21e07..34fe4dbbb6 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.h +++ b/interface/src/ui/overlays/Grid3DOverlay.h @@ -35,7 +35,7 @@ public: virtual Grid3DOverlay* createClone() const override; // Grids are UI tools, and may not be intersected (pickable) - virtual bool findRayIntersection(const glm::vec3&, const glm::vec3&, float&, BoxFace&, glm::vec3&) override { return false; } + virtual bool findRayIntersection(const glm::vec3&, const glm::vec3&, float&, BoxFace&, glm::vec3&, bool precisionPicking = false) override { return false; } protected: Transform evalRenderTransform() override; diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 6e9946e935..a4ce7f9e0d 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -258,7 +258,7 @@ void Image3DOverlay::setURL(const QString& url) { } bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal) { + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. Transform transform = getTransform(); diff --git a/interface/src/ui/overlays/Image3DOverlay.h b/interface/src/ui/overlays/Image3DOverlay.h index aa802a82a9..4432e3b07c 100644 --- a/interface/src/ui/overlays/Image3DOverlay.h +++ b/interface/src/ui/overlays/Image3DOverlay.h @@ -43,7 +43,7 @@ public: bool isTransparent() override { return Base3DOverlay::isTransparent() || _alphaTexture; } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal) override; + BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; virtual Image3DOverlay* createClone() const override; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 875352ebb4..f4289b1bf5 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -509,16 +509,16 @@ QVariant ModelOverlay::getProperty(const QString& property) { } bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal) { + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { QVariantMap extraInfo; - return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo); + return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking); } bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo) { + float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) { - return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo); + return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking); } ModelOverlay* ModelOverlay::createClone() const { diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 334c9c06f1..f7a79c5615 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -45,9 +45,9 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal) override; + BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo) override; + float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) override; virtual ModelOverlay* createClone() const override; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 85041aad4e..be7b0d42fc 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -554,7 +554,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay glm::vec3 thisSurfaceNormal; QVariantMap thisExtraInfo; if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance, - thisFace, thisSurfaceNormal, thisExtraInfo)) { + thisFace, thisSurfaceNormal, thisExtraInfo, precisionPicking)) { bool isDrawInFront = thisOverlay->getDrawInFront(); if ((bestIsFront && isDrawInFront && thisDistance < bestDistance) || (!bestIsFront && (isDrawInFront || thisDistance < bestDistance))) { diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index f53d06a239..9a436c7564 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -71,7 +71,7 @@ QVariant Planar3DOverlay::getProperty(const QString& property) { } bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal) { + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { // FIXME - face and surfaceNormal not being returned return findRayRectangleIntersection(origin, direction, getWorldOrientation(), getWorldPosition(), getDimensions(), distance); } diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 0a0e75696e..e2a0e1f896 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -31,7 +31,7 @@ public: virtual QVariant getProperty(const QString& property) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal) override; + BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; protected: glm::vec2 _dimensions; diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index 3aed2a5b42..cf1f7f7fcb 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -76,7 +76,7 @@ QVariant Volume3DOverlay::getProperty(const QString& property) { } bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal) { + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { // extents is the entity relative, scaled, centered extents of the entity glm::mat4 worldToEntityMatrix; Transform transform = getTransform(); diff --git a/interface/src/ui/overlays/Volume3DOverlay.h b/interface/src/ui/overlays/Volume3DOverlay.h index bde8c71aef..e9b996a6dd 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.h +++ b/interface/src/ui/overlays/Volume3DOverlay.h @@ -31,7 +31,7 @@ public: QVariant getProperty(const QString& property) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal) override; + BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; protected: // Centered local bounding box diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 8af818edc6..ade478347c 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -622,7 +622,7 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) { } } -bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { +bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) { glm::vec2 dimensions = getDimensions(); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition(); diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index d888424cbc..2cf35c0172 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -53,7 +53,7 @@ public: QVariant getProperty(const QString& property) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal) override; + BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; virtual Web3DOverlay* createClone() const override; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 012a891698..502874fbfb 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -815,8 +815,13 @@ unsigned int LimitedNodeList::broadcastToNodes(std::unique_ptr packet, eachNode([&](const SharedNodePointer& node){ if (node && destinationNodeTypes.contains(node->getType())) { - sendUnreliablePacket(*packet, *node); - ++n; + if (packet->isReliable()) { + auto packetCopy = NLPacket::createCopy(*packet); + sendPacket(std::move(packetCopy), *node); + } else { + sendUnreliablePacket(*packet, *node); + } + ++n; } }); diff --git a/scripts/developer/tests/ControlsGallery.qml b/scripts/developer/tests/ControlsGallery.qml new file mode 100644 index 0000000000..ceb8a26dc9 --- /dev/null +++ b/scripts/developer/tests/ControlsGallery.qml @@ -0,0 +1,103 @@ +import QtQuick 2.10 +import QtQuick.Window 2.10 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "qrc:////qml//styles-uit" as HifiStylesUit +import "qrc:////qml//controls-uit" as HifiControlsUit + +//uncomment to use from qmlscratch tool +//import '../../../interface/resources/qml/controls-uit' as HifiControlsUit +//import '../../../interface/resources/qml/styles-uit' + +//uncomment to use with HIFI_USE_SOURCE_TREE_RESOURCES=1 +//import '../../../resources/qml/controls-uit' as HifiControlsUit +//import '../../../resources/qml/styles-uit' + +Item { + visible: true + width: 640 + height: 480 + + Introspector { + id: introspector + properties: ['realFrom', 'realTo', 'realValue', 'realStepSize', 'decimals'] + visible: true + y: 50 + x: 130 + } + + HifiStylesUit.HifiConstants { + id: hifi + } + + TabBar { + id: bar + width: parent.width + TabButton { + text: "Spinbox" + } + TabButton { + text: "... Other Controls" + } + } + + StackLayout { + id: controlsLayout + currentIndex: bar.currentIndex + anchors.top: bar.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 20 + + Item { + id: spinboxTab + anchors.fill: parent + + Column { + spacing: 20 + + HifiControlsUit.SpinBox { + realValue: 5.0 + realFrom: 16.0 + realTo: 20.0 + decimals: 2 + realStepSize: 0.01 + + width: 100 + height: 30 + + colorScheme: hifi.colorSchemes.dark + + onFocusChanged: { + if(focus) { + introspector.object = this + } + } + } + + HifiControlsUit.SpinBox { + realValue: 5.0 + realFrom: 1.0 + realTo: 20.0 + decimals: 2 + realStepSize: 0.01 + + width: 100 + height: 30 + + colorScheme: hifi.colorSchemes.light + + onFocusChanged: { + if(focus) { + introspector.object = this + } + } + } + } + } + Item { + id: otherTab + } + } +} diff --git a/scripts/developer/tests/Introspector.qml b/scripts/developer/tests/Introspector.qml new file mode 100644 index 0000000000..d21f5da976 --- /dev/null +++ b/scripts/developer/tests/Introspector.qml @@ -0,0 +1,166 @@ +import QtQuick 2.1; +import QtQuick.Window 2.1; + +MouseArea { + id: base; + opacity: 0.65; + // anchors.fill: parent; + width: 400; + height: 300; + + drag.target: list; + onWheel: { } + + onClicked: { object = null } + property var object: null + onObjectChanged: { + visible = (object != null) + } + + property var properties: [] + onPropertiesChanged: { + console.debug('properties: ', JSON.stringify(properties, 4, 0)) + } + + function getPropertiesList(obj) { + var props = []; + var propertiesObject = obj; + if(properties.length !== 0) { + propertiesObject = {}; + for(var i = 0; i < properties.length; ++i) { + propertiesObject[properties[i]] = properties[i]; + } + } + + for(var prop in propertiesObject) { + + var info = {'name' : prop}; + var value = obj[prop]; + var typeOfValue = typeof(value); + + if(typeof(value) === 'string') { + info['type'] = 'string' + } else if(typeof(value) === 'number') { + if(Number.isInteger(value)) + info['type'] = 'int' + else + info['type'] = 'float' + } else if(typeof(value) === 'boolean') { + info['type'] = 'boolean' + } else if(typeof(value) === 'function') { + continue; + } + + /* + if(prop !== 'parent' && prop !== 'data' && prop !== 'children') + console.debug('typeof(value): ', typeof(value), JSON.stringify(value, null, 4)); + */ + + info['subName'] = '' + props.push(info); + } + + return props; + } + + Rectangle { + color: "lightgray"; + anchors.fill: list; + anchors.margins: -50; + } + ListView { + id: list; + x: 50; + y: 50; + width: 400; + height: 300; + spacing: 5; + model: object !== null ? getPropertiesList(object) : []; + header: Text { + text: object !== null ? object.toString () : ''; + font.bold: true; + font.pixelSize: 20; + } + delegate: Row { + spacing: 20; + + Column { + width: 180; + + Text { + text: (modelData ["subName"] !== "" ? (modelData ["name"] + "." + modelData ["subName"]) : modelData ["name"]); + font.pixelSize: 16; + } + } + Column { + width: 200; + + Text { + text: { + return modelData ["type"] + } + font.pixelSize: 10; + } + TextInput { + id: input; + text: display; + width: parent.width; + font.pixelSize: 16; + font.underline: (text !== display); + Keys.onReturnPressed: { save (); } + Keys.onEnterPressed: { save (); } + Keys.onEscapePressed: { cancel (); } + + property string display : ""; + + function save () { + var tmp; + switch (modelData ["type"]) { + case 'boolean': + tmp = (text === "true" || text === "1"); + break; + case 'float': + tmp = parseFloat (text); + break; + case 'int': + tmp = parseInt (text); + break; + case 'string': + tmp = text; + break; + + default: + break; + } + if (modelData ["subName"] !== "") { + object [modelData ["name"]][modelData ["subName"]] = tmp; + } + else { + object [modelData ["name"]] = tmp; + } + text = display; + } + + function cancel () { + text = display; + } + + Binding on text { value: input.display; } + Binding on display { + value: { + var ret = (modelData ["subName"] !== "" + ? object [modelData ["name"]][modelData ["subName"]] + : object [modelData ["name"]]); + return ret.toString (); + } + } + Rectangle { + z: -1; + color: "white"; + anchors.fill: parent; + } + } + } + } + } +} diff --git a/scripts/developer/tests/controlsGallery.js b/scripts/developer/tests/controlsGallery.js new file mode 100644 index 0000000000..dc3fa7ba3c --- /dev/null +++ b/scripts/developer/tests/controlsGallery.js @@ -0,0 +1,23 @@ +(function() { // BEGIN LOCAL_SCOPE + + console.debug('controlsGallery: creating window') + + var qml = Script.resolvePath('ControlsGallery.qml'); + var qmlWindow = new OverlayWindow({ + title: 'Hifi Controls Gallery', + source: qml, + height: 480, + width: 640, + visible: true + }); + + console.debug('controlsGallery: creating window... done') + + qmlWindow.closed.connect(function() { Script.stop(); }); + + Script.scriptEnding.connect(function() { + console.debug('controlsGallery: end of scripting') + delete qmlWindow; + }); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/assets/animations/Cheering.fbx b/scripts/system/assets/animations/Cheer.fbx similarity index 100% rename from scripts/system/assets/animations/Cheering.fbx rename to scripts/system/assets/animations/Cheer.fbx diff --git a/scripts/system/assets/animations/Clapping.fbx b/scripts/system/assets/animations/Clap.fbx similarity index 100% rename from scripts/system/assets/animations/Clapping.fbx rename to scripts/system/assets/animations/Clap.fbx diff --git a/scripts/system/assets/animations/Crying.fbx b/scripts/system/assets/animations/Cry.fbx similarity index 67% rename from scripts/system/assets/animations/Crying.fbx rename to scripts/system/assets/animations/Cry.fbx index 2e60ba2450..7ecd6415a5 100644 Binary files a/scripts/system/assets/animations/Crying.fbx and b/scripts/system/assets/animations/Cry.fbx differ diff --git a/scripts/system/assets/animations/Dancing.fbx b/scripts/system/assets/animations/Dance.fbx similarity index 100% rename from scripts/system/assets/animations/Dancing.fbx rename to scripts/system/assets/animations/Dance.fbx diff --git a/scripts/system/assets/animations/Love.fbx b/scripts/system/assets/animations/Love.fbx new file mode 100644 index 0000000000..159ccafd04 Binary files /dev/null and b/scripts/system/assets/animations/Love.fbx differ diff --git a/scripts/system/assets/animations/Pointing.fbx b/scripts/system/assets/animations/Point.fbx similarity index 100% rename from scripts/system/assets/animations/Pointing.fbx rename to scripts/system/assets/animations/Point.fbx diff --git a/scripts/system/assets/animations/Sit1.fbx b/scripts/system/assets/animations/Sit1.fbx new file mode 100644 index 0000000000..db75219980 Binary files /dev/null and b/scripts/system/assets/animations/Sit1.fbx differ diff --git a/scripts/system/assets/animations/Sit2.fbx b/scripts/system/assets/animations/Sit2.fbx new file mode 100644 index 0000000000..400b599794 Binary files /dev/null and b/scripts/system/assets/animations/Sit2.fbx differ diff --git a/scripts/system/assets/animations/Sit3.fbx b/scripts/system/assets/animations/Sit3.fbx new file mode 100644 index 0000000000..174fd75c4e Binary files /dev/null and b/scripts/system/assets/animations/Sit3.fbx differ diff --git a/scripts/system/assets/animations/Waving.fbx b/scripts/system/assets/animations/Wave.fbx similarity index 100% rename from scripts/system/assets/animations/Waving.fbx rename to scripts/system/assets/animations/Wave.fbx diff --git a/scripts/system/emote.js b/scripts/system/emote.js index 139870fd63..87fc86d569 100644 --- a/scripts/system/emote.js +++ b/scripts/system/emote.js @@ -16,9 +16,11 @@ (function() { // BEGIN LOCAL_SCOPE -var EMOTE_ANIMATIONS = ['Crying', 'Surprised', 'Dancing', 'Cheering', 'Waving', 'Fall', 'Pointing', 'Clapping']; +var EMOTE_ANIMATIONS = ['Cry', 'Surprised', 'Dance', 'Cheer', 'Wave', 'Fall', 'Point', 'Clap', 'Sit1', 'Sit2', 'Sit3', 'Love']; var ANIMATIONS = Array(); +var eventMappingName = "io.highfidelity.away"; // restoreAnimation on hand controller button events, too +var eventMapping = Controller.newMapping(eventMappingName); EMOTE_ANIMATIONS.forEach(function (name) { var animationURL = Script.resolvePath("assets/animations/" + name + ".fbx"); @@ -31,16 +33,15 @@ EMOTE_ANIMATIONS.forEach(function (name) { var EMOTE_APP_BASE = "html/EmoteApp.html"; var EMOTE_APP_URL = Script.resolvePath(EMOTE_APP_BASE); var EMOTE_LABEL = "EMOTE"; -var EMOTE_APP_SORT_ORDER = 11; +var EMOTE_APP_SORT_ORDER = 12; var FPS = 60; var MSEC_PER_SEC = 1000; -var FINISHED = 3; // see ScriptableResource::State var onEmoteScreen = false; var button; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -var activeTimer = false; // used to cancel active timer if a user plays an amimation while another animation is playing -var activeEmote = false; // to keep track of the currently playing emote +var activeTimer = false; // Used to cancel active timer if a user plays an animation while another animation is playing +var activeEmote = false; // To keep track of the currently playing emote button = tablet.addButton({ icon: "icons/tablet-icons/EmoteAppIcon.svg", @@ -58,7 +59,7 @@ function onClicked() { } function onScreenChanged(type, url) { - onEmoteScreen = type === "Web" && (url.indexOf(EMOTE_APP_BASE) == url.length - EMOTE_APP_BASE.length); + onEmoteScreen = type === "Web" && (url.indexOf(EMOTE_APP_BASE) === url.length - EMOTE_APP_BASE.length); button.editProperties({ isActive: onEmoteScreen }); } @@ -71,34 +72,79 @@ function onWebEventReceived(event) { } if (event.type === "click") { + var emoteName = event.data; - - if (ANIMATIONS[emoteName].resource.state == FINISHED) { - if (activeTimer !== false) { - Script.clearTimeout(activeTimer); + + if (activeTimer !== false) { + Script.clearTimeout(activeTimer); + } + + // If the activeEmote is different from the chosen emote, then play the new emote. Other wise, + // This is a second click on the same emote as the activeEmote, and we will just stop it. + if (activeEmote !== emoteName) { + activeEmote = emoteName; + + // Allow for a random sitting animation when a user selects sit + var randSit = Math.floor(Math.random() * 3) + 1; + if (emoteName === "Sit"){ + emoteName = event.data + randSit; // "Sit1, Sit2, Sit3" } - - // if the activeEmote is different from the chosen emote, then play the new emote. Other wise, - // this is a second click on the same emote as the activeEmote, and we will just stop it. - if (activeEmote !== emoteName) { - activeEmote = emoteName; - var frameCount = ANIMATIONS[emoteName].animation.frames.length; + + var frameCount = ANIMATIONS[emoteName].animation.frames.length; + + // Three types of emotes (non-looping end, non-looping return, looping) + if (emoteName.match(/^Sit.*$/) || emoteName === "Fall") { // non-looping end + + MyAvatar.overrideAnimation(ANIMATIONS[emoteName].url, FPS, false, 0, frameCount); + + // Non-looping return + } else if (emoteName === "Love" || emoteName === "Surprised" || emoteName === "Cry" || emoteName === "Point"){ + MyAvatar.overrideAnimation(ANIMATIONS[emoteName].url, FPS, false, 0, frameCount); - var timeOut = MSEC_PER_SEC * frameCount / FPS; activeTimer = Script.setTimeout(function () { MyAvatar.restoreAnimation(); activeTimer = false; activeEmote = false; }, timeOut); - } else { - activeEmote = false; - MyAvatar.restoreAnimation(); + + } else { // Looping + + MyAvatar.overrideAnimation(ANIMATIONS[emoteName].url, FPS, true, 0, frameCount); + } + + } else { + activeEmote = false; + MyAvatar.restoreAnimation(); } + + } } +// If a user provides input, end the emote animation and restore the navigation animation states (idle, walk, run) +function restoreAnimation() { + MyAvatar.restoreAnimation(); +} + +Controller.keyPressEvent.connect(restoreAnimation); + +// Note peek() so as to not interfere with other mappings. +eventMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.LB).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.LS).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.LeftGrip).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.RB).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.RS).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.RightGrip).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.Back).peek().to(restoreAnimation); +eventMapping.from(Controller.Standard.Start).peek().to(restoreAnimation); +Controller.enableMapping(eventMappingName); + button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); tablet.webEventReceived.connect(onWebEventReceived); diff --git a/scripts/system/html/EmoteApp.html b/scripts/system/html/EmoteApp.html index 0a423b9b6c..16dee490a9 100644 --- a/scripts/system/html/EmoteApp.html +++ b/scripts/system/html/EmoteApp.html @@ -38,7 +38,7 @@ .content { margin-top: 90px; - padding: 30px; + padding: 10px 30px; } input[type=button] { @@ -47,9 +47,9 @@ font-size: 20px; text-transform: uppercase; vertical-align: top; - height: 105px; + height: 90px; min-width: 190px; - padding: 0px 18px; + padding: 0px 10px; margin-right: 6px; border-radius: 5px; border: none; @@ -97,15 +97,17 @@

Emote App

-

Click an emotion to Emote:

-

+

Choose an emote:

+

-

-

-

+

+

+

-

-

+

+

+

+

diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index fade6f6b26..532f58493f 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -15,6 +15,7 @@ Script.include(Script.resolvePath("../libraries/controllers.js")); Script.include(Script.resolvePath("../libraries/Xform.js")); var Y_AXIS = {x: 0, y: 1, z: 0}; +var X_AXIS = {x: 1, y: 0, z: 0}; var DEFAULT_DPI = 31; var DEFAULT_WIDTH = 0.4375; var DEFAULT_VERTICAL_FIELD_OF_VIEW = 45; // degrees @@ -33,10 +34,16 @@ var DELAY_FOR_30HZ = 33; // milliseconds // will need to be recaclulated if dimensions of fbx model change. var TABLET_NATURAL_DIMENSIONS = {x: 32.083, y: 48.553, z: 2.269}; -var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png"; +var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "images/button-close.png"; // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png"; // var TABLET_MODEL_PATH = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx"; +var LOCAL_BEZEL_HIGHLIGHT = Script.resourcesPath() + "images/buttonBezel_highlight.png"; +var LOCAL_NORMAL_BEZEL = Script.resourcesPath() + "images/buttonBezel.png"; + var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-button-small-bezel.fbx"; +var HIGH_PRIORITY = 1; +var LOW_PRIORITY = 0; +var SUBMESH = 2; // returns object with two fields: // * position - position in front of the user @@ -134,11 +141,10 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { Overlays.deleteOverlay(this.webOverlayID); } - var RAYPICK_OFFSET = 0.0007; // Sufficient for raypick to reliably intersect tablet screen before tablet model. - var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) / sensorScaleFactor + RAYPICK_OFFSET; - var WEB_ENTITY_Y_OFFSET = 1 * tabletScaleFactor; - var screenWidth = 0.9275 * tabletWidth; - var screenHeight = 0.8983 * tabletHeight; + var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.5) * sensorScaleFactor; + var WEB_ENTITY_Y_OFFSET = 1.25 * tabletScaleFactor; + var screenWidth = 0.9367 * tabletWidth; + var screenHeight = 0.9000 * tabletHeight; this.webOverlayID = Overlays.addOverlay("web3d", { name: "WebTablet Web", url: url, @@ -154,12 +160,14 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { visible: visible }); - var HOME_BUTTON_Y_OFFSET = (tabletHeight / 2) - (tabletHeight / 20) + 0.003 * sensorScaleFactor; // FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here var homeButtonDim = 4.0 * tabletScaleFactor / 3.0; + var HOME_BUTTON_X_OFFSET = 0.00079 * sensorScaleFactor; + var HOME_BUTTON_Y_OFFSET = -1 * ((tabletHeight / 2) - (4.0 * tabletScaleFactor / 2)); + var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleFactor; this.homeButtonID = Overlays.addOverlay("circle3d", { name: "homeButton", - localPosition: { x: 0.0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, + localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET }, localRotation: { x: 0, y: 1, z: 0, w: 0}, dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }, solid: true, @@ -170,24 +178,46 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { parentJointIndex: -1 }); - this.homeButtonHighlightID = Overlays.addOverlay("circle3d", { - name: "homeButtonHighlight", - localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, - localRotation: { x: 0, y: 1, z: 0, w: 0 }, - dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }, - color: { red: 255, green: 255, blue: 255 }, - solid: true, - innerRadius: 0.9, - ignoreIntersection: true, - alpha: 1.0, - visible: visible, - drawInFront: false, - parentID: this.tabletEntityID, - parentJointIndex: -1 - }); + this.homeButtonUnhighlightMaterial = Entities.addEntity({ + type: "Material", + materialURL: "materialData", + localPosition: { x: 0.0, y: 0.0, z: 0.0 }, + priority: HIGH_PRIORITY, + materialData: JSON.stringify({ + materials: { + albedoMap: LOCAL_NORMAL_BEZEL + } + + }), + userData: JSON.stringify({ + "grabbableKey": {"grabbable": false} + }), + visible: false, + parentMaterialName: SUBMESH, + parentID: this.tabletEntityID + }, true); + + this.homeButtonHighlightMaterial = Entities.addEntity({ + type: "Material", + materialURL: "materialData", + localPosition: { x: 0.0, y: 0.0, z: 0.0 }, + priority: LOW_PRIORITY, + visible: false, + materialData: JSON.stringify({ + materials: { + emissiveMap: LOCAL_BEZEL_HIGHLIGHT + } + + }), + userData: JSON.stringify({ + "grabbableKey": {"grabbable": false} + }), + parentMaterialName: SUBMESH, + parentID: this.tabletEntityID + }, true); this.receive = function (channel, senderID, senderUUID, localOnly) { - if (_this.homeButtonID == senderID) { + if (_this.homeButtonID === senderID) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var onHomeScreen = tablet.onHomeScreen(); var isMessageOpen; @@ -339,7 +369,8 @@ WebTablet.prototype.destroy = function () { Overlays.deleteOverlay(this.webOverlayID); Overlays.deleteOverlay(this.tabletEntityID); Overlays.deleteOverlay(this.homeButtonID); - Overlays.deleteOverlay(this.homeButtonHighlightID); + Entities.deleteEntity(this.homeButtonUnhighlightMaterial); + Entities.deleteEntity(this.homeButtonHighlightMaterial); HMD.displayModeChanged.disconnect(this.myOnHmdChanged); Controller.mousePressEvent.disconnect(this.myMousePressEvent); @@ -433,21 +464,24 @@ WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function (windowPos WebTablet.prototype.onHoverEnterOverlay = function (overlayID, pointerEvent) { if (overlayID === this.homeButtonID) { - Overlays.editOverlay(this.homeButtonHighlightID, { alpha: 1.0 }); + Entities.editEntity(this.homeButtonUnhighlightMaterial, {priority: LOW_PRIORITY}); + Entities.editEntity(this.homeButtonHighlightMaterial, {priority: HIGH_PRIORITY}); } -} +}; WebTablet.prototype.onHoverOverOverlay = function (overlayID, pointerEvent) { if (overlayID !== this.homeButtonID) { - Overlays.editOverlay(this.homeButtonHighlightID, { alpha: 0.0 }); + Entities.editEntity(this.homeButtonUnhighlightMaterial, {priority: HIGH_PRIORITY}); + Entities.editEntity(this.homeButtonHighlightMaterial, {priority: LOW_PRIORITY}); } -} +}; WebTablet.prototype.onHoverLeaveOverlay = function (overlayID, pointerEvent) { if (overlayID === this.homeButtonID) { - Overlays.editOverlay(this.homeButtonHighlightID, { alpha: 0.0 }); + Entities.editEntity(this.homeButtonUnhighlightMaterial, {priority: HIGH_PRIORITY}); + Entities.editEntity(this.homeButtonHighlightMaterial, {priority: LOW_PRIORITY}); } -} +}; // compute position, rotation & parentJointIndex of the tablet WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMouse, tabletProperties) { @@ -575,22 +609,6 @@ WebTablet.prototype.scheduleMouseMoveProcessor = function() { } }; -WebTablet.prototype.handleHomeButtonHover = function(x, y) { - var pickRay = Camera.computePickRay(x, y); - var entityPickResults; - var homebuttonHovered = false; - entityPickResults = Overlays.findRayIntersection(pickRay, true, [this.tabletEntityID]); - if (entityPickResults.intersects && (entityPickResults.entityID === this.tabletEntityID || - entityPickResults.overlayID === this.tabletEntityID)) { - var overlayPickResults = Overlays.findRayIntersection(pickRay, true, [this.homeButtonID], []); - if (overlayPickResults.intersects && overlayPickResults.overlayID === this.homeButtonID) { - homebuttonHovered = true; - } - } - - Overlays.editOverlay(this.homeButtonHighlightID, { alpha: homebuttonHovered ? 1.0 : 0.0 }); -} - WebTablet.prototype.mouseMoveEvent = function (event) { if (this.dragging) { this.currentMouse = { @@ -598,8 +616,6 @@ WebTablet.prototype.mouseMoveEvent = function (event) { y: event.y }; this.scheduleMouseMoveProcessor(); - } else { - this.handleHomeButtonHover(event.x, event.y); } }; @@ -626,8 +642,6 @@ WebTablet.prototype.mouseMoveProcessor = function () { }); } this.scheduleMouseMoveProcessor(); - } else { - this.handleHomeButtonHover(this.currentMouse.x, this.currentMouse.y); } }; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 1506ce17b2..5dfb0d5b69 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -129,7 +129,8 @@ DISPATCHER_PROPERTIES = [ "userData", "type", "href", - "cloneable" + "cloneable", + "cloneDynamic" ]; // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index 7e9e1d7e6a..220ecd1959 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -32,7 +32,7 @@ if (!Function.prototype.bind) { if (this.prototype) { // Function.prototype doesn't have a prototype property - fNOP.prototype = this.prototype; + fNOP.prototype = this.prototype; } fBound.prototype = new fNOP(); @@ -370,7 +370,7 @@ getTabletWidthFromSettings = function () { resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) { - if (!HMD.tabletID || !HMD.tabletScreenID || !HMD.homeButtonID || !HMD.homeButtonHighlightID) { + if (!HMD.tabletID || !HMD.tabletScreenID || !HMD.homeButtonID) { return; } var sensorScaleFactor = sensorToWorldScaleOverride || MyAvatar.sensorToWorldScale; @@ -381,6 +381,7 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) sensorScaleOffsetOverride = 1 / sensorScaleFactor; } + // will need to be recaclulated if dimensions of fbx model change. var TABLET_NATURAL_DIMENSIONS = {x: 32.083, y: 48.553, z: 2.269}; var DEFAULT_DPI = 31; @@ -399,32 +400,26 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) }); // update webOverlay - var RAYPICK_OFFSET = 0.0007; // Sufficient for raypick to reliably intersect tablet screen before tablet model. - var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) * sensorScaleOffsetOverride + RAYPICK_OFFSET; - var WEB_ENTITY_Y_OFFSET = 1 * tabletScaleFactor; - print(WEB_ENTITY_Y_OFFSET); - var screenWidth = 0.9275 * tabletWidth; - var screenHeight = 0.8983 * tabletHeight; + var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.5) * sensorScaleOffsetOverride; + var WEB_ENTITY_Y_OFFSET = 1.25 * tabletScaleFactor * sensorScaleOffsetOverride; + var screenWidth = 0.9367 * tabletWidth; + var screenHeight = 0.9000 * tabletHeight; var landscape = Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape; Overlays.editOverlay(HMD.tabletScreenID, { - localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, + localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET}, dimensions: {x: landscape ? screenHeight : screenWidth, y: landscape ? screenWidth : screenHeight, z: 0.1}, dpi: tabletDpi }); // update homeButton - var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20) + 0.003 * sensorScaleFactor) * sensorScaleOffsetOverride; // FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here var homeButtonDim = 4.0 * tabletScaleFactor / 3.0; + var HOME_BUTTON_X_OFFSET = 0.00079 * sensorScaleOffsetOverride * sensorScaleFactor; + var HOME_BUTTON_Y_OFFSET = -1 * ((tabletHeight / 2) - (4.0 * tabletScaleFactor / 2)) * sensorScaleOffsetOverride; + var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride; Overlays.editOverlay(HMD.homeButtonID, { - localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, - localRotation: Quat.angleAxis(180, Vec3.UNIT_Y), - dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } - }); - - Overlays.editOverlay(HMD.homeButtonHighlightID, { - localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, - localRotation: Quat.angleAxis(180, Vec3.UNIT_Y), + localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET }, + localRotation: { x: 0, y: 1, z: 0, w: 0 }, dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); }; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 208e64fd5e..fd7b9c703a 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -114,7 +114,6 @@ var selectionDisplay = null; // for gridTool.js to ignore Overlays.editOverlay(HMD.tabletID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); Overlays.editOverlay(HMD.homeButtonID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.homeButtonHighlightIDtabletID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); Overlays.editOverlay(HMD.tabletScreenID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); } diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index ee3dab7308..29dc457197 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -24,7 +24,7 @@ var validCheckTime = Date.now(); var debugTablet = false; var tabletScalePercentage = 70.0; - UIWebTablet = null; + var UIWebTablet = null; var MSECS_PER_SEC = 1000.0; var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone"; var gTablet = null; @@ -103,8 +103,9 @@ UIWebTablet.register(); HMD.tabletID = UIWebTablet.tabletEntityID; HMD.homeButtonID = UIWebTablet.homeButtonID; - HMD.homeButtonHighlightID = UIWebTablet.homeButtonHighlightID; HMD.tabletScreenID = UIWebTablet.webOverlayID; + HMD.homeButtonHighlightMaterialID = UIWebTablet.homeButtonHighlightMaterial; + HMD.homeButtonUnhighlightMaterialID = UIWebTablet.homeButtonUnhighlightMaterial; HMD.displayModeChanged.connect(onHmdChanged); MyAvatar.sensorToWorldScaleChanged.connect(onSensorToWorldScaleChanged); @@ -130,7 +131,6 @@ tabletProperties.visible = true; Overlays.editOverlay(HMD.tabletID, tabletProperties); Overlays.editOverlay(HMD.homeButtonID, { visible: true }); - Overlays.editOverlay(HMD.homeButtonHighlightID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 90 }); updateTabletWidthFromSettings(true); @@ -151,7 +151,6 @@ Overlays.editOverlay(HMD.tabletID, { visible: false }); Overlays.editOverlay(HMD.homeButtonID, { visible: false }); - Overlays.editOverlay(HMD.homeButtonHighlightID, { visible: false }); Overlays.editOverlay(HMD.tabletScreenID, { visible: false }); Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 1 }); } @@ -172,7 +171,6 @@ UIWebTablet = null; HMD.tabletID = null; HMD.homeButtonID = null; - HMD.homeButtonHighlightID = null; HMD.tabletScreenID = null; } else if (debugTablet) { print("TABLET closeTabletUI, UIWebTablet is null"); @@ -325,7 +323,8 @@ Overlays.deleteOverlay(tabletID); HMD.tabletID = null; HMD.homeButtonID = null; - HMD.homeButtonHighlightID = null; HMD.tabletScreenID = null; + HMD.homeButtonHighlightMaterialID = null; + HMD.homeButtonUnhighlightMaterialID = null; }); }()); // END LOCAL_SCOPE diff --git a/server-console/package.json b/server-console/package.json index 6dd39ea6f8..565658702b 100644 --- a/server-console/package.json +++ b/server-console/package.json @@ -27,7 +27,7 @@ "cheerio": "^0.19.0", "electron-log": "1.1.1", "extend": "^3.0.0", - "fs-extra": "^1.0.0", + "fs-extra": "^6.0.0", "node-notifier": "^5.2.1", "os-homedir": "^1.0.1", "request": "^2.85.0", diff --git a/server-console/src/main.js b/server-console/src/main.js index e145a2b76b..ac4b0411e7 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -115,17 +115,43 @@ const UPDATER_LOCK_FULL_PATH = getRootHifiDataDirectory() + "/" + UPDATER_LOCK_F // Configure log global.log = require('electron-log'); -const logFile = getApplicationDataDirectory(true) + '/log.txt'; +const oldLogFile = path.join(getApplicationDataDirectory(), '/log.txt'); +const logFile = path.join(getApplicationDataDirectory(true), '/log.txt'); +if (oldLogFile != logFile && fs.existsSync(oldLogFile)) { + if (!fs.existsSync(oldLogFile)) { + fs.moveSync(oldLogFile, logFile); + } else { + fs.remove(oldLogFile); + } +} fs.ensureFileSync(logFile); // Ensure file exists log.transports.file.maxSize = 5 * 1024 * 1024; log.transports.file.file = logFile; log.debug("build info", buildInfo); log.debug("Root hifi directory is: ", getRootHifiDataDirectory()); +log.debug("App Data directory:", getApplicationDataDirectory()); +fs.ensureDirSync(getApplicationDataDirectory()); + +var oldLogPath = path.join(getApplicationDataDirectory(), '/logs'); +var logPath = path.join(getApplicationDataDirectory(true), '/logs'); +if (oldLogPath != logPath && fs.existsSync(oldLogPath)) { + if (!fs.existsSync(oldLogPath)) { + fs.moveSync(oldLogPath, logPath); + } else { + fs.remove(oldLogPath); + } +} +fs.ensureDirSync(logPath); +log.debug("Log directory:", logPath); + +const configPath = path.join(getApplicationDataDirectory(), 'config.json'); +var userConfig = new Config(); +userConfig.load(configPath); + const ipcMain = electron.ipcMain; - var isShuttingDown = false; function shutdown() { log.debug("Normal shutdown (isShuttingDown: " + isShuttingDown + ")"); @@ -232,27 +258,6 @@ function deleteOldFiles(directoryPath, maxAgeInSeconds, filenameRegex) { } } -var oldLogPath = path.join(getApplicationDataDirectory(), '/logs'); -var logPath = path.join(getApplicationDataDirectory(true), '/logs'); - -if (oldLogPath != logPath) { - console.log("Migrating old logs from " + oldLogPath + " to " + logPath); - fs.copy(oldLogPath, logPath, err => { - if (err) { - console.error(err); - } else { - console.log('success!'); - } - }) -} - -log.debug("Log directory:", logPath); -log.debug("Data directory:", getRootHifiDataDirectory()); - -const configPath = path.join(getApplicationDataDirectory(), 'config.json'); -var userConfig = new Config(); -userConfig.load(configPath); - // print out uncaught exceptions in the console process.on('uncaughtException', function(err) { log.error(err); diff --git a/tools/jsdoc/.gitignore b/tools/jsdoc/.gitignore index c585e19389..148363ca03 100644 --- a/tools/jsdoc/.gitignore +++ b/tools/jsdoc/.gitignore @@ -1 +1,2 @@ -out \ No newline at end of file +out + diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index 4a6c18f243..1c4333983f 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -8,7 +8,7 @@ set(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/node_modules/.bin/jsdoc JSDOC_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/config.json JSDOC_CONFIG_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/out OUTPUT_DIR) -file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) +file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/root.js NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} COMMAND ${NPM_EXECUTABLE} --no-progress install && ${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR} diff --git a/tools/jsdoc/README.md b/tools/jsdoc/README.md index 5cce6bb2a6..5cdb1ea44e 100644 --- a/tools/jsdoc/README.md +++ b/tools/jsdoc/README.md @@ -2,12 +2,27 @@ ##Prerequisites -* Install node.js +* Install node.js. * Install jsdoc via npm. `npm install jsdoc -g` +If you would like the extra functionality for gravPrep: +* Run npm install + To generate html documentation for the High Fidelity JavaScript API: * `cd tools/jsdoc` * `jsdoc . -c config.json` The out folder should contain index.html. + +To generate the grav automation files, run node gravPrep.js after you have made a JSdoc output folder. + +This will create files that are needed for hifi-grav and hifi-grav-content repos + +The md files for hifi-grav-content are located in out/grav/06.api-reference. + +The template twig html files for hifi-grav are located out/grav/templates. + +if you would like to copy these to a local version of the docs on your system you can run with the follows args: + +* node grav true "path/to/grav/" "path/to/grav/content" \ No newline at end of file diff --git a/tools/jsdoc/gravPrep.js b/tools/jsdoc/gravPrep.js new file mode 100644 index 0000000000..849837bae0 --- /dev/null +++ b/tools/jsdoc/gravPrep.js @@ -0,0 +1,671 @@ +// Dependencies +const htmlclean = require('htmlclean'); +const fs = require('fs'); +const path = require('path'); +const pretty = require('pretty'); +const cheerio = require('cheerio'); +const rimraf = require('rimraf'); +const dedent = require('dedent-js'); + +// Arg Vars +const copyLocal = process.argv[2]; +console.log("copyLocal:", copyLocal); +let targetTemplateDirectory = ''; +let targetMDDirectory = ''; +if (copyLocal) { + targetTemplateDirectory = process.argv[3]; + targetMDDirectory = process.argv[4];; +} + +// Required directories +let dir_out = path.join(__dirname, 'out'); + +let dir_grav = path.join(dir_out, 'grav'); +let dir_css = path.join(dir_grav, 'css'); +let dir_js = path.join(dir_grav, 'js'); +let dir_template = path.join(dir_grav, 'templates'); + +let dir_md = path.join(dir_grav, '06.api-reference'); +let dir_md_objects = path.join(dir_md, '02.Objects'); +let dir_md_namespaces = path.join(dir_md, '01.Namespaces'); +let dir_md_globals = path.join(dir_md, '03.Globals'); + +// Array to itterate over and create if doesn't exist +let dirArray = [dir_grav, dir_css, dir_js, dir_template, dir_md, dir_md_objects, dir_md_namespaces, dir_md_globals]; + +// Base Grouping Directories for MD files +let baseMDDirectories = ["API-Reference", "Globals", "Namespaces", "Objects"]; + +// Maps for directory names +let map_dir_md = { + "API-Reference": dir_md, + "Globals": dir_md_globals, + "Objects": dir_md_objects, + "Namespaces": dir_md_namespaces, + "Class": dir_md_objects, + "Namespace": dir_md_namespaces, + "Global": dir_md_globals +} + +// Map for Links +let map_links = { + "Global": "globals", + "Namespace": "namespaces", + "Class": "objects" +} + +// Mapping for GroupNames and Members +let groupNameMemberMap = { + "Objects": [], + "Namespaces": [], + "Globals": [] +} + +// Html variables to be handle regex replacements +const html_reg_static = /\(static\)<\/span>/g +const html_reg_title = /\.+?\<\/h1\>/g; +const html_reg_htmlExt = /\.html/g; +const html_reg_objectHeader = /
[\s\S]+?<\/header>/; +const html_reg_objectSpanNew = /
<\/h5>/; +const html_reg_propertiesHeaderEdit = '

Properties:

'; +const html_reg_propertiesHeaderEdit_Replace = '

Properties

'; +const html_reg_typeEdit = /(
Returns[\s\S]*?Type)(<\/dt[\s\S]*?type">)(.*?)(<\/span><\/dd>[\s\S]*?<\/dl>)/g; +const html_reg_typeEdit_replace = '$1: $3' +const html_reg_methodSize = /()/g; +const html_reg_methodSize_replace = ''; +const html_reg_findByName = '
` +const html_reg_signalTitle = `

Signals

`; +const html_reg_typeDefinitonsTitle = /

Type Definitions<\/h3>/; +const html_reg_typeDefinitonsTitle_replace = `

Type Definitions

` +const html_reg_classDefinitonsTitle = /

Classes<\/h3>/; +const html_reg_classDefinitonsTitle_replace = `

Classes

` +const html_reg_firstDivClose = ``; +const html_reg_allNonHTTPLinks = /()/g; +const html_reg_allHTTPLinks = /()/g; +const html_reg_pretty = /(
)([\s\S]*?)(<\/pre>)/g;
+const html_reg_pretty_replace = "
$2<\/pre>";
+const html_reg_availableIn = /([\s\S]+?Available in:[\s\S]+?<\/table>)/g;
+const html_reg_findControllerCuratedList = /
Functions<\/h5>[\s\S]*?

Input Recordings[\s\S]*?<\/ul>/g +const html_reg_findEntityMethods = /

Entity Methods:[\s\S]+?<\/ul>/g; +const html_reg_EntityMethodsHeader = '
Entity Methods:
'; +const html_reg_EntityMethodsHeader_replace = '
Entity Methods
'; +const html_reg_dlClassDetails = /
<\/dl>/g +const html_reg_typeDefType = /(
)(Type:)(<\/h5>[\s\S]*?)([\s\S]*?<\/ul>)/g; +const html_reg_typeDefType_replace = `
$2 $4
`; +const html_reg_returnSize = /
Returns:<\/h5>/g; +const html_reg_returnSize_replace = '
Returns:<\/h6>'; +const html_reg_depreciated = /(
[\s\S]+?)(
)([\s\S]+?)([\s\S]+?)(<\/ul>[\s\S]+?)(<\/dd>)/g; +const html_reg_depreciated_replace = '$1
$4
' + +// Procedural functions + +//remove .html from non http links +function removeHTML(match, p1, p2, p3) { + p2 = p2.replace(".html", ""); + return [p1, p2, p3].join(""); +} + +// Turn links to lower case that aren't part of IDs +function allLinksToLowerCase(match, p1, p2, p3) { + // split on id # and make sure only the preceding is lower case + if (p2.indexOf("#") > -1) { + p2 = p2.split("#"); + p2 = [p2[0].toLowerCase(), "#", p2[1]].join(""); + } else { + p2 = p2.toLowerCase(); + } + return [p1, p2, p3].join(""); +} + +// Helper for fixing formatting of page links +function fixLinkGrouping(match, p1, p2, p3) { + // Handle if referencing ID + let count = (p2.match(/\./g) || []).length; + if (p2.indexOf("#") > -1) { + let split = p2.split("#"); + if (count >= 2) { + // console.log("MULTI DOTS!"); + split = p2.split("."); + // This is a case where we are in an object page and there are multiple levels referenced (only doing 2 levels at the moment) + // console.log("split", split) + return [p1, "/api-reference/", returnRightGroup(split[1].slice(0, -1)), "/", split[1], ".", split[2], p3].join(""); + } + if (split[0] === "global") { + return [p1, "/api-reference/", "globals", "#", split[1], p3].join(""); + } + return [p1, "/api-reference/", returnRightGroup(split[0]), "/", p2, p3].join(""); + } else { + // Handle if there are member references + // console.log("count", count) + let split; + if (count === 1) { + split = p2.split("."); + return [p1, "/api-reference/", returnRightGroup(split[1]), "/", split[1], p3].join(""); + } + return [p1, "/api-reference/", returnRightGroup(p2), "/", p2, p3].join(""); + } +} + +// Return the right group for where the method or type came from +function returnRightGroup(methodToCheck) { + for (var key in groupNameMemberMap) { + for (i = 0; i < groupNameMemberMap[key].length; i++) { + if (methodToCheck.toLowerCase() === groupNameMemberMap[key][i].toLowerCase()) { + return key.toLowerCase(); + } else { + // console.log("Couldn't find group: ", methodToCheck); + } + } + } +} + +// Create the actual MD file +function createMD(title, directory, needsDir, isGlobal) { + let mdSource = makeMdSource(title); + + if (needsDir) { + if (!fs.existsSync(directory)) { + fs.mkdirSync(directory); + } + } + + let destinationMDFile = path.join(directory, `API_${title}.md`); + fs.writeFileSync(destinationMDFile, mdSource); +} + +// Create the actual Template file +function createTemplate(title, content) { + let twigBasePartial = makeTwigFile(content); + let destinationFile = path.join(dir_template, `API_${title}.html.twig`); + fs.writeFileSync(destinationFile, twigBasePartial); +} + +// Copy file from source to target - used for recurssive call +function copyFileSync(source, target) { + let targetFile = target; + + // If target is a directory a new file with the same name will be created + if (fs.existsSync(target)) { + if (fs.lstatSync(target).isDirectory()) { + targetFile = path.join(target, path.basename(source)); + } + } + + fs.writeFileSync(targetFile, fs.readFileSync(source)); +} + +// Copy file from source to target +function copyFolderRecursiveSync(source, target) { + var files = []; + + // Check if folder needs to be created or integrated + var targetFolder = path.join(target, path.basename(source)); + if (!fs.existsSync(targetFolder)) { + fs.mkdirSync(targetFolder); + } + + // Copy + if (fs.lstatSync(source).isDirectory()) { + files = fs.readdirSync(source); + files.forEach(function(file) { + var curSource = path.join(source, file); + if (fs.lstatSync(curSource).isDirectory()) { + copyFolderRecursiveSync(curSource, targetFolder); + } else { + copyFileSync(curSource, targetFolder); + } + }); + } +} + +// Clean up the Html +function prepareHtml(source) { + let htmlBefore = fs.readFileSync(source, { encoding: 'utf8' }); + let htmlAfter = htmlclean(htmlBefore); + let htmlAfterPretty = pretty(htmlAfter); + return cheerio.load(htmlAfterPretty); +} + +// Base file for MD's +function makeMdSource(title) { + return dedent( + ` + --- + title: ${title} + taxonomy: + category: + - docs + visible: true + highlight: + enabled: false + --- + ` + ) +} + +// Base file for Templates +function makeTwigFile(contentHtml) { + return dedent( + ` + {% extends 'partials/base_noGit.html.twig' %} + {% set tags = page.taxonomy.tag %} + {% if tags %} + {% set progress = page.collection({'items':{'@taxonomy':{'category': 'docs', 'tag': tags}},'order': {'by': 'default', 'dir': 'asc'}}) %} + {% else %} + {% set progress = page.collection({'items':{'@taxonomy':{'category': 'docs'}},'order': {'by': 'default', 'dir': 'asc'}}) %} + {% endif %} + + {% block navigation %} + + {% endblock %} + + {% block content %} +
+
+

{{ page.title }}

+ ${contentHtml} +
+
+ {% endblock %} + ` + ) +} + +// Handle NameSpace Group +function handleNamespace(title, content) { + let destinationDirectory = path.join(map_dir_md["Namespace"], title); + createMD(title, destinationDirectory, true); + createTemplate(title, content); +} + +// Handle Class Group +function handleClass(title, content) { + let destinationDirectory = path.join(map_dir_md["Class"], title); + createMD(title, destinationDirectory, true) + + let formatedHtml = content + .replace(html_reg_objectSpanNew, "") + createTemplate(title, formatedHtml); +} + +// Handle Global Group +function handleGlobal(title, content) { + createMD("Globals", map_dir_md["Global"], false, true); + createTemplate("Globals", content); +} + +// Handle Group TOCs +function makeGroupTOC(group) { + let mappedGroup; + if (!Array.isArray(group)) { + mappedGroup = groupNameMemberMap[group]; + } else { + mappedGroup = group; + } + let htmlGroup = mappedGroup.map(item => { + return dedent( + ` +
+ ${item} +
+ ` + ) + }) + return htmlGroup.join("\n"); + } + +// Handle Class TOCS +function makeClassTOC(group){ + let linkArray = [] + group.forEach( item => { + linkArray.push(`
${item.type}
`) + item.array.forEach( link => { + if ( link.indexOf('.') > -1 ){ + linkArray.push(``); + } else { + linkArray.push(``); + + } + }) + linkArray.push("
"); + }) + return linkArray.join("\n"); +} + +// Extract IDS for TOC +function extractIDs(groupToExtract){ + let firstLine = ""; + let id = ""; + let extractedIDs = []; + groupToExtract.forEach((item)=>{ + firstLine = item.split("\n")[0]; + try { + id = firstLine.split('id="')[1].split(`"`)[0]; + } catch (e){ + id = ""; + } + if (id){ + extractedIDs.push(id) + } + }) + return extractedIDs; +} + +// Helper for splitting up html +// Takes: Content to split, SearchTerm to Split by, and term to End Splitting By +// Returns: [newContent after Split, Array of extracted ] +function splitBy(content, searchTerm, endSplitTerm, title){ + let foundArray = []; + let curIndex = -1; + let afterCurSearchIndex = -1 + let nextIndex = 0; + let findbyNameLength = searchTerm.length; + let curEndSplitTermIndex = -1; + let classHeader; + do { + // Find the index of where to stop searching + curEndSplitTermIndex = content.indexOf(endSplitTerm); + // console.log("curEndSplitTermIndex", curEndSplitTermIndex) + // Find the index of the the next Search term + curIndex = content.indexOf(searchTerm); + // console.log("curIndex", curIndex) + + // The index of where the next search will start + afterCurSearchIndex = curIndex+findbyNameLength; + // Find the content of the next Index + nextIndex = content.indexOf(searchTerm,afterCurSearchIndex); + // If the next index isn't found, then next index === index of the end term + if (nextIndex === -1){ + nextIndex = curEndSplitTermIndex; + } + if (curIndex > curEndSplitTermIndex){ + break; + } + // Push from the cur index to the next found || the end term + let contentSlice = content.slice(curIndex, nextIndex); + if (contentSlice.indexOf(`id="${title}"`) === -1){ + foundArray.push(contentSlice); + } else { + classHeader = contentSlice; + } + + // Remove that content + content = content.replace(contentSlice, ""); + + curEndSplitTermIndex = content.indexOf(endSplitTerm); + nextIndex = content.indexOf(searchTerm,afterCurSearchIndex); + // Handle if nextIndex goes beyond endSplitTerm + if (nextIndex > curEndSplitTermIndex) { + curIndex = content.indexOf(searchTerm); + contentSlice = content.slice(curIndex, curEndSplitTermIndex); + if (contentSlice.indexOf(`id="${title}"`) === -1){ + foundArray.push(contentSlice); + } + content = content.replace(contentSlice, ""); + break; + } + } while (curIndex > -1) + if (classHeader){ + content = append(content, html_reg_findByArticleClose, classHeader, true); + } + return [content, foundArray]; +} + +// Split the signals and methods [Might make this more generic] +function splitMethodsSignals(allItemToSplit){ + let methodArray = []; + let signalArray = []; + + allItemToSplit.forEach( (content, index) => { + firstLine = content.split("\n")[0]; + if (firstLine.indexOf("{Signal}") > -1){ + signalArray.push(content); + } else if (firstLine.indexOf("span") > -1) { + methodArray.push(content); + } else { + } + }) + return [methodArray, signalArray]; +} + +// Helper to append +// Takes content, the search term to appendTo, the content to append, +// and bool if the append is before the found area +function append(content, searchTermToAppendto, contentToAppend, appendBefore){ + let contentArray = content.split("\n"); + let foundIndex = findArrayTrim(contentArray, searchTermToAppendto) + foundIndex = appendBefore ? foundIndex : foundIndex +1 + + contentArray.splice(foundIndex,0,contentToAppend) + return contentArray.join("\n") +} + +// Helper function for append +function findArrayTrim(array, searchTerm){ + var index = -1; + for (var i = 0; i < array.length; i++){ + index = array[i].trim().indexOf(searchTerm.trim()); + if (index > -1){ + return i + } + } + return index; +} + +// Remove grav directory if exists to make sure old files aren't kept +if (fs.existsSync(dir_grav)){ + console.log("dir_grav exists"); + rimraf.sync(dir_grav); +} + +// Create Grav directories in JSDOC output +dirArray.forEach(function(dir){ + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } +}) + +// Create baseMD files +baseMDDirectories.forEach( md => { + createMD(md, map_dir_md[md]); +}) + +// Read jsdoc output folder and process html files +let files = fs.readdirSync(dir_out); + +// Create initial Group name member map to handle individual link +files.forEach(function (file){ + let curSource = path.join(dir_out, file); + if (path.extname(curSource) == ".html" && path.basename(curSource, '.html') !== "index") { + let loadedHtml = prepareHtml(curSource); + let splitTitle = loadedHtml("title").text().split(": "); + let groupName = splitTitle[1]; + let htmlTitle = splitTitle.pop(); + switch(groupName){ + case "Namespace": + groupNameMemberMap["Namespaces"].push(htmlTitle); + break; + case "Class": + groupNameMemberMap["Objects"].push(htmlTitle); + break; + default: + console.log(`Case not handled for ${groupName}`); + } + } +}) +files.forEach(function (file, index){ + // For testing individual files + // if (index !== 59) return; + let curSource = path.join(dir_out, file); + if (path.extname(curSource) == ".html" && path.basename(curSource, '.html') !== "index") { + // Clean up the html source + let loadedHtml = prepareHtml(curSource); + + // Extract the title, group name, and the main div + let splitTitle = loadedHtml("title").text().split(": "); + let groupName = splitTitle[1]; + let htmlTitle = splitTitle.pop(); + console.log("html title", htmlTitle) + let mainDiv = loadedHtml("#main") + + let methodIDs = []; + let signalIDs = []; + let typeDefIDs = []; + // Basic Regex HTML edits + let currentContent = mainDiv.html() + .replace(html_reg_findByMethod, "") //Remove Method title to be remade later + .replace(html_reg_static,"") // Remove static from the file names + .replace(html_reg_title,"") // Remove title + .replace(html_reg_objectHeader,"") // Remove extra Object Header + .replace(html_reg_dlClassDetails, "") // Remove unneccsary dlClassDetails Tag + .replace(html_reg_allNonHTTPLinks, removeHTML) // Remove the .html extension from all links + .replace(html_reg_allNonHTTPLinks, allLinksToLowerCase) // Turn all links into lowercase before ID tags + .replace(html_reg_allNonHTTPLinks, fixLinkGrouping) // Make sure links refer to correct grouping + .replace(html_reg_propertiesHeaderEdit, html_reg_propertiesHeaderEdit_Replace) // Remove : from Properties + .replace(html_reg_typeEdit, html_reg_typeEdit_replace) // Put type on the same line + .replace(html_reg_returnSize, html_reg_returnSize_replace) // Make return size h6 instead of h5 + .replace(html_reg_methodSize, html_reg_methodSize_replace) // Make method size into h5 + .replace(html_reg_pretty, html_reg_pretty_replace) // remove the references to pretty + .replace(html_reg_classDefinitonsTitle, html_reg_classDefinitonsTitle_replace) // Change the class def titles + .replace(html_reg_depreciated, html_reg_depreciated_replace); // format depreciated better + + // Further HTML Manipulation + // Make end term either Type Definitions or by the article + let endTerm; + let foundTypeDefinitions; + let foundSignalsAndMethods; + if (currentContent.indexOf("Type Definitions") > -1){ + // console.log("Found Type Definitions"); + endTerm = `

Type Definitions

`; + // Split HTML by Each named entry + let contentSplitArray = splitBy(currentContent, html_reg_findByName, endTerm, htmlTitle); + foundSignalsAndMethods = contentSplitArray[1]; + // console.log("foundSignalsAndMethods", foundSignalsAndMethods) + // Create a reference to the current content after split and the split functions + currentContent = contentSplitArray[0] + .replace(html_reg_typeDefType, html_reg_typeDefType_replace) // Edit how the typedef type looks + .replace(html_reg_typeDefinitonsTitle, ""); // Remove Type Definitions Title to be remade later; + endTerm = html_reg_findByArticleClose; + // Grab split Type Definitions + let contentSplitArrayForTypeDefs = splitBy(currentContent, html_reg_findByName, endTerm, htmlTitle); + currentContent = contentSplitArrayForTypeDefs[0]; + foundTypeDefinitions = contentSplitArrayForTypeDefs[1]; + // console.log("foundTypeDefinitions", foundTypeDefinitions) + + } else { + endTerm = html_reg_findByArticleClose; + let contentSplitArray = splitBy(currentContent, html_reg_findByName, endTerm, htmlTitle); + foundSignalsAndMethods = contentSplitArray[1]; + currentContent = contentSplitArray[0]; + } + + // Create references to the split methods and signals + let processedMethodsSignalsAndTypeDefs = splitMethodsSignals(foundSignalsAndMethods); + let splitMethods = processedMethodsSignalsAndTypeDefs[0]; + let splitSignals = processedMethodsSignalsAndTypeDefs[1]; + let splitTypeDefinitionIDS; + let splitMethodIDS = extractIDs(splitMethods); + let splitSignalIDS = extractIDs(splitSignals); + if (foundTypeDefinitions){ + splitTypeDefinitionIDS = extractIDs(foundTypeDefinitions); + } + let arrayToPassToClassToc = []; + + if (splitMethods.length > 0) { + arrayToPassToClassToc.push({type: "Methods", array: splitMethodIDS}); + // Add the Methods header to the Methods HTML + splitMethods.unshift(html_reg_findByMethod_replace) + currentContent = append(currentContent, html_reg_findByArticleClose, splitMethods.join('\n'), true); + } + if (splitSignals.length > 0) { + arrayToPassToClassToc.push({type: "Signals", array: splitSignalIDS}); + // Add the Signals header to the Signals HTML + splitSignals.unshift(html_reg_signalTitle) + currentContent = append(currentContent, html_reg_findByArticleClose, splitSignals.join('\n'),true); + } + if (foundTypeDefinitions && foundTypeDefinitions.length > 0) { + arrayToPassToClassToc.push({type: "Type Definitions", array: splitTypeDefinitionIDS}); + // Add the Type Defs header to the Type Defs HTML + foundTypeDefinitions.unshift(html_reg_typeDefinitonsTitle_replace) + currentContent = append(currentContent, html_reg_findByArticleClose, foundTypeDefinitions.join('\n'), true); + } + + let classTOC = makeClassTOC(arrayToPassToClassToc); + if (groupName === "Global"){ + currentContent = append(currentContent, html_reg_findByTitle, classTOC); + } else if (htmlTitle === "Controller") { + let curatedList = currentContent.match(html_reg_findControllerCuratedList); + currentContent = currentContent.replace(html_reg_findControllerCuratedList, ""); + let entityMethods = currentContent.match(html_reg_findEntityMethods); + currentContent = currentContent.replace(html_reg_findEntityMethods, ""); + currentContent = append(currentContent, html_reg_firstDivClose, [classTOC, curatedList, entityMethods].join("\n")); + currentContent = currentContent.replace(html_reg_EntityMethodsHeader, html_reg_EntityMethodsHeader_replace); + } else { + currentContent = append(currentContent, html_reg_firstDivClose, classTOC); + } + + // Final Pretty Content + currentContent = htmlclean(currentContent); + currentContent = pretty(currentContent); + + // Handle Unique Categories + switch(groupName){ + case "Namespace": + handleNamespace(htmlTitle, currentContent); + break; + case "Class": + handleClass(htmlTitle, currentContent); + break; + case "Global": + handleGlobal(htmlTitle, currentContent); + break; + default: + console.log(`Case not handled for ${groupName}`); + } + } +}) + +// Create the base Templates after processing individual files +createTemplate("API-Reference", makeGroupTOC(["Namespaces", "Objects", "Globals"])); +createTemplate("Namespaces", makeGroupTOC("Namespaces")); +createTemplate("Objects", makeGroupTOC("Objects")); + +// Copy the files to the target Directories if Local +if (copyLocal){ + // Copy files to the Twig Directory + let templateFiles = fs.readdirSync(path.resolve(targetTemplateDirectory)); + // Remove Existing API files + templateFiles.forEach(function(file){ + let curSource = path.join(targetTemplateDirectory, file); + + if(path.basename(file, '.html').indexOf("API") > -1){ + fs.unlink(curSource); + } + + }) + copyFolderRecursiveSync(dir_template, targetTemplateDirectory); + + // Copy files to the Md Directory + let baseMdRefDir = path.join(targetMDDirectory,"06.api-reference"); + // Remove existing MD directory + if (fs.existsSync(baseMdRefDir)){ + rimraf.sync(baseMdRefDir); + } + copyFolderRecursiveSync(dir_md, targetMDDirectory); +} \ No newline at end of file diff --git a/tools/jsdoc/package.json b/tools/jsdoc/package.json index 215ceec177..4bbb2ad4f2 100644 --- a/tools/jsdoc/package.json +++ b/tools/jsdoc/package.json @@ -1,7 +1,14 @@ { "name": "hifiJSDoc", "dependencies": { - "jsdoc": "^3.5.5" + "axios": "^0.18.0", + "cheerio": "^1.0.0-rc.2", + "dedent-js": "^1.0.1", + "htmlclean": "^3.0.8", + "jsdoc": "^3.5.5", + "pretty": "^2.0.0", + "request": "^2.85.0", + "rimraf": "^2.6.2" }, "private": true } diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index 049d32cceb..76f33e2c73 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -153,4 +153,4 @@ exports.defineTags = function (dictionary) { doclet.hifiServerEntity = true; } }); -}; +}; \ No newline at end of file