diff --git a/interface/resources/icons/tablet-icons/mic-clip-i.svg b/interface/resources/icons/tablet-icons/mic-clip-i.svg new file mode 100644 index 0000000000..f912c1e744 --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic-clip-i.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/mic-gate-i.svg b/interface/resources/icons/tablet-icons/mic-gate-i.svg new file mode 100644 index 0000000000..8255174532 --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic-gate-i.svg @@ -0,0 +1,3 @@ + + + diff --git a/interface/resources/icons/tablet-icons/mic-mute-a.svg b/interface/resources/icons/tablet-icons/mic-mute-a.svg index 9dc2c53443..67eafc27c8 100644 --- a/interface/resources/icons/tablet-icons/mic-mute-a.svg +++ b/interface/resources/icons/tablet-icons/mic-mute-a.svg @@ -1,25 +1,3 @@ - - - - - - - - - - - - - - - + + diff --git a/interface/resources/icons/tablet-icons/mic-mute-i.svg b/interface/resources/icons/tablet-icons/mic-mute-i.svg index 9dc2c53443..63af1b0da8 100644 --- a/interface/resources/icons/tablet-icons/mic-mute-i.svg +++ b/interface/resources/icons/tablet-icons/mic-mute-i.svg @@ -1,25 +1,3 @@ - - - - - - - - - - - - - - - + + diff --git a/interface/resources/icons/tablet-icons/mic-mute.svg b/interface/resources/icons/tablet-icons/mic-mute.svg deleted file mode 100644 index bd42fded05..0000000000 --- a/interface/resources/icons/tablet-icons/mic-mute.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/interface/resources/icons/tablet-icons/mic-unmute-a.svg b/interface/resources/icons/tablet-icons/mic-unmute-a.svg index b1464f207d..0bf7677017 100644 --- a/interface/resources/icons/tablet-icons/mic-unmute-a.svg +++ b/interface/resources/icons/tablet-icons/mic-unmute-a.svg @@ -1,70 +1,3 @@ - - - -image/svg+xml \ No newline at end of file + + + diff --git a/interface/resources/icons/tablet-icons/mic-unmute-i.svg b/interface/resources/icons/tablet-icons/mic-unmute-i.svg index c4eda55cbc..0bf7677017 100644 --- a/interface/resources/icons/tablet-icons/mic-unmute-i.svg +++ b/interface/resources/icons/tablet-icons/mic-unmute-i.svg @@ -1,22 +1,3 @@ - - - - - - - - - - - - - + + diff --git a/interface/resources/icons/tablet-icons/mic.svg b/interface/resources/icons/tablet-icons/mic.svg index 30b46d18dd..c961351b14 100644 --- a/interface/resources/icons/tablet-icons/mic.svg +++ b/interface/resources/icons/tablet-icons/mic.svg @@ -59,4 +59,4 @@ d="m 27.9,20.9 c 0,0 0,-3.6 0,-3.8 0,-0.7 -0.6,-1.2 -1.3,-1.2 -0.7,0 -1.2,0.6 -1.2,1.3 0,0.2 0,3.4 0,3.7 0,2.6 -2.4,4.8 -5.3,4.8 -2.9,0 -5.3,-2.1 -5.3,-4.8 0,-0.3 0,-3.5 0,-3.8 0,-0.7 -0.5,-1.3 -1.2,-1.3 -0.7,0 -1.3,0.5 -1.3,1.2 0,0.2 0,3.9 0,3.9 0,3.6 2.9,6.6 6.6,7.2 l 0,2.4 -3.1,0 c -0.7,0 -1.3,0.6 -1.3,1.3 0,0.7 0.6,1.3 1.3,1.3 l 8.8,0 c 0.7,0 1.3,-0.6 1.3,-1.3 0,-0.7 -0.6,-1.3 -1.3,-1.3 l -3.2,0 0,-2.4 c 3.6,-0.5 6.5,-3.5 6.5,-7.2 z" id="path6952" inkscape:connector-curvature="0" - style="fill:#ffffff" /> \ No newline at end of file + style="fill:#ffffff" /> diff --git a/interface/resources/qml/AvatarInputsBar.qml b/interface/resources/qml/AvatarInputsBar.qml index 4a071d2d04..3f1f598991 100644 --- a/interface/resources/qml/AvatarInputsBar.qml +++ b/interface/resources/qml/AvatarInputsBar.qml @@ -7,24 +7,57 @@ // import Hifi 1.0 as Hifi -import QtQuick 2.4 +import QtQuick 2.5 +import QtGraphicalEffects 1.0 import "./hifi/audio" as HifiAudio +import TabletScriptingInterface 1.0 + Item { id: root; objectName: "AvatarInputsBar" property int modality: Qt.NonModal - width: audio.width; - height: audio.height; - x: 10; y: 5; - + readonly property bool ignoreRadiusEnabled: AvatarInputs.ignoreRadiusEnabled; + x: 10; + y: 5; readonly property bool shouldReposition: true; + property bool hmdActive: HMD.active; + width: hmdActive ? audio.width : audioApplication.width; + height: hmdActive ? audio.height : audioApplication.height; + + Timer { + id: hmdActiveCheckTimer; + interval: 500; + repeat: true; + onTriggered: { + root.hmdActive = HMD.active; + } + + } HifiAudio.MicBar { id: audio; - visible: AvatarInputs.showAudioTools; + visible: AvatarInputs.showAudioTools && root.hmdActive; standalone: true; - dragTarget: parent; + dragTarget: parent; + } + + HifiAudio.MicBarApplication { + id: audioApplication; + visible: AvatarInputs.showAudioTools && !root.hmdActive; + standalone: true; + dragTarget: parent; + } + + Component.onCompleted: { + HMD.displayModeChanged.connect(function(isHmdMode) { + root.hmdActive = isHmdMode; + }); + } + + BubbleIcon { + dragTarget: parent + visible: !root.hmdActive; } } diff --git a/interface/resources/qml/BubbleIcon.qml b/interface/resources/qml/BubbleIcon.qml new file mode 100644 index 0000000000..f4e99f136c --- /dev/null +++ b/interface/resources/qml/BubbleIcon.qml @@ -0,0 +1,100 @@ +// +// Created by Bradley Austin Davis on 2015/06/19 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.5 +import QtGraphicalEffects 1.0 + +import "./hifi/audio" as HifiAudio + +import TabletScriptingInterface 1.0 + +Rectangle { + id: bubbleRect + width: bubbleIcon.width + 10 + height: bubbleIcon.height + 10 + radius: 5; + property var dragTarget: null; + property bool ignoreRadiusEnabled: AvatarInputs.ignoreRadiusEnabled; + + function updateOpacity() { + if (ignoreRadiusEnabled) { + bubbleRect.opacity = 1.0; + } else { + bubbleRect.opacity = 0.7; + } + } + + Component.onCompleted: { + updateOpacity(); + } + + onIgnoreRadiusEnabledChanged: { + updateOpacity(); + } + + color: "#00000000"; + border { + width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0; + color: "#80FFFFFF"; + } + anchors { + left: dragTarget ? dragTarget.right : undefined + top: dragTarget ? dragTarget.top : undefined + } + + // borders are painted over fill, so reduce the fill to fit inside the border + Rectangle { + color: "#55000000"; + width: 40; + height: 40; + + radius: 5; + + anchors { + verticalCenter: parent.verticalCenter; + horizontalCenter: parent.horizontalCenter; + } + } + + MouseArea { + id: mouseArea; + anchors.fill: parent + + hoverEnabled: true; + scrollGestureEnabled: false; + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + Users.toggleIgnoreRadius(); + } + drag.target: dragTarget; + onContainsMouseChanged: { + var rectOpacity = (ignoreRadiusEnabled && containsMouse) ? 1.0 : (containsMouse ? 1.0 : 0.7); + if (containsMouse) { + Tablet.playSound(TabletEnums.ButtonHover); + } + bubbleRect.opacity = rectOpacity; + } + } + Image { + id: bubbleIcon + source: "../icons/tablet-icons/bubble-i.svg"; + sourceSize: Qt.size(32, 32); + smooth: true; + anchors.top: parent.top + anchors.topMargin: (parent.height - bubbleIcon.height) / 2 + anchors.left: parent.left + anchors.leftMargin: (parent.width - bubbleIcon.width) / 2 + } + ColorOverlay { + id: bubbleIconOverlay + anchors.fill: bubbleIcon + source: bubbleIcon + color: "#FFFFFF"; + } +} diff --git a/interface/resources/qml/controlsUit/CheckBoxQQC2.qml b/interface/resources/qml/controlsUit/CheckBoxQQC2.qml index 91d35ecd58..bd71025aa9 100644 --- a/interface/resources/qml/controlsUit/CheckBoxQQC2.qml +++ b/interface/resources/qml/controlsUit/CheckBoxQQC2.qml @@ -24,6 +24,7 @@ CheckBox { leftPadding: 0 property int colorScheme: hifi.colorSchemes.light property string color: hifi.colors.lightGrayText + property int fontSize: hifi.fontSizes.inputLabel readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light property bool isRedCheck: false property bool isRound: false @@ -109,7 +110,7 @@ CheckBox { contentItem: Text { id: root - font.pixelSize: hifi.fontSizes.inputLabel + font.pixelSize: fontSize; font.family: "Raleway" font.weight: Font.DemiBold text: checkBox.text diff --git a/interface/resources/qml/controlsUit/Switch.qml b/interface/resources/qml/controlsUit/Switch.qml index 4e1c21c456..422b08b4eb 100644 --- a/interface/resources/qml/controlsUit/Switch.qml +++ b/interface/resources/qml/controlsUit/Switch.qml @@ -21,6 +21,7 @@ Item { property int switchWidth: 70; readonly property int switchRadius: height/2; property string labelTextOff: ""; + property int labelTextSize: hifi.fontSizes.inputLabel; property string labelGlyphOffText: ""; property int labelGlyphOffSize: 32; property string labelTextOn: ""; @@ -89,7 +90,7 @@ Item { RalewaySemiBold { id: labelOff; text: labelTextOff; - size: hifi.fontSizes.inputLabel; + size: labelTextSize; color: originalSwitch.checked ? hifi.colors.lightGrayText : "#FFFFFF"; anchors.top: parent.top; anchors.right: parent.right; @@ -130,7 +131,7 @@ Item { RalewaySemiBold { id: labelOn; text: labelTextOn; - size: hifi.fontSizes.inputLabel; + size: labelTextSize; color: originalSwitch.checked ? "#FFFFFF" : hifi.colors.lightGrayText; anchors.top: parent.top; anchors.left: parent.left; diff --git a/interface/resources/qml/hifi/EditAvatarInputsBar.qml b/interface/resources/qml/hifi/EditAvatarInputsBar.qml new file mode 100644 index 0000000000..b27b0c8db2 --- /dev/null +++ b/interface/resources/qml/hifi/EditAvatarInputsBar.qml @@ -0,0 +1,152 @@ +// +// EditAvatarInputsBar.qml +// qml/hifi +// +// Audio setup +// +// Created by Wayne Chen on 3/20/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 + +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit +import "../windows" + +Rectangle { + id: editRect + + HifiConstants { id: hifi; } + + color: hifi.colors.baseGray; + + signal sendToScript(var message); + function emitSendToScript(message) { + sendToScript(message); + } + + function fromScript(message) { + } + + RalewayRegular { + id: title; + color: hifi.colors.white; + text: qsTr("Avatar Inputs Persistent UI Settings") + size: 20 + font.bold: true + anchors { + top: parent.top + left: parent.left + leftMargin: (parent.width - width) / 2 + } + } + + HifiControlsUit.Slider { + id: xSlider + anchors { + top: title.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + label: "X OFFSET: " + value.toFixed(2); + maximumValue: 1.0 + minimumValue: -1.0 + stepSize: 0.05 + value: -0.2 + width: 300 + onValueChanged: { + emitSendToScript({ + "method": "reposition", + "x": value + }); + } + } + + HifiControlsUit.Slider { + id: ySlider + anchors { + top: xSlider.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + label: "Y OFFSET: " + value.toFixed(2); + maximumValue: 1.0 + minimumValue: -1.0 + stepSize: 0.05 + value: -0.125 + width: 300 + onValueChanged: { + emitSendToScript({ + "method": "reposition", + "y": value + }); + } + } + + HifiControlsUit.Slider { + id: zSlider + anchors { + top: ySlider.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + label: "Z OFFSET: " + value.toFixed(2); + maximumValue: 0.0 + minimumValue: -1.0 + stepSize: 0.05 + value: -0.5 + width: 300 + onValueChanged: { + emitSendToScript({ + "method": "reposition", + "z": value + }); + } + } + + HifiControlsUit.Button { + id: setVisibleButton; + text: setVisible ? "SET INVISIBLE" : "SET VISIBLE"; + width: 300; + property bool setVisible: true; + anchors { + top: zSlider.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + onClicked: { + setVisible = !setVisible; + emitSendToScript({ + "method": "setVisible", + "visible": setVisible + }); + } + } + + HifiControlsUit.Button { + id: printButton; + text: "PRINT POSITIONS"; + width: 300; + anchors { + top: setVisibleButton.bottom + topMargin: 50 + left: parent.left + leftMargin: 20 + } + onClicked: { + emitSendToScript({ + "method": "print", + }); + } + } +} diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 195dd78a0e..f7e2494813 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -31,6 +31,8 @@ Rectangle { property string title: "Audio Settings" property int switchHeight: 16 property int switchWidth: 40 + property bool pushToTalk: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD; + property bool muted: (bar.currentIndex === 0) ? AudioScriptingInterface.mutedDesktop : AudioScriptingInterface.mutedHMD; readonly property real verticalScrollWidth: 10 readonly property real verticalScrollShaft: 8 signal sendToScript(var message); @@ -44,7 +46,7 @@ Rectangle { property bool isVR: AudioScriptingInterface.context === "VR" - property real rightMostInputLevelPos: 440 + property real rightMostInputLevelPos: root.width //placeholder for control sizes and paddings //recalculates dynamically in case of UI size is changed QtObject { @@ -103,7 +105,9 @@ Rectangle { } } - Component.onCompleted: enablePeakValues(); + Component.onCompleted: { + enablePeakValues(); + } Flickable { id: flickView; @@ -178,15 +182,25 @@ Rectangle { height: root.switchHeight; switchWidth: root.switchWidth; labelTextOn: "Mute microphone"; + labelTextSize: 16; backgroundOnColor: "#E3E3E3"; - checked: AudioScriptingInterface.muted; + checked: muted; onClicked: { - if (AudioScriptingInterface.pushToTalk && !checked) { + if (pushToTalk && !checked) { // disable push to talk if unmuting - AudioScriptingInterface.pushToTalk = false; + if (bar.currentIndex === 0) { + AudioScriptingInterface.pushToTalkDesktop = false; + } + else { + AudioScriptingInterface.pushToTalkHMD = false; + } + } + if (bar.currentIndex === 0) { + AudioScriptingInterface.mutedDesktop = checked; + } + else { + AudioScriptingInterface.mutedHMD = checked; } - AudioScriptingInterface.muted = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding } } @@ -198,6 +212,7 @@ Rectangle { anchors.topMargin: 24 anchors.left: parent.left labelTextOn: "Noise Reduction"; + labelTextSize: 16; backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.noiseReduction; onCheckedChanged: { @@ -213,7 +228,8 @@ Rectangle { anchors.top: noiseReductionSwitch.bottom anchors.topMargin: 24 anchors.left: parent.left - labelTextOn: qsTr("Push To Talk (T)"); + labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk"); + labelTextSize: 16; backgroundOnColor: "#E3E3E3"; checked: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD; onCheckedChanged: { @@ -222,13 +238,6 @@ Rectangle { } else { AudioScriptingInterface.pushToTalkHMD = checked; } - checked = Qt.binding(function() { - if (bar.currentIndex === 0) { - return AudioScriptingInterface.pushToTalkDesktop; - } else { - return AudioScriptingInterface.pushToTalkHMD; - } - }); // restore binding } } } @@ -245,7 +254,8 @@ Rectangle { switchWidth: root.switchWidth; anchors.top: parent.top anchors.left: parent.left - labelTextOn: qsTr("Warn when muted"); + labelTextOn: qsTr("Warn when muted in HMD"); + labelTextSize: 16; backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.warnWhenMuted; onClicked: { @@ -263,6 +273,7 @@ Rectangle { anchors.topMargin: 24 anchors.left: parent.left labelTextOn: qsTr("Audio Level Meter"); + labelTextSize: 16; backgroundOnColor: "#E3E3E3"; checked: AvatarInputs.showAudioTools; onCheckedChanged: { @@ -279,6 +290,7 @@ Rectangle { anchors.topMargin: 24 anchors.left: parent.left labelTextOn: qsTr("Stereo input"); + labelTextSize: 16; backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.isStereoInput; onCheckedChanged: { @@ -314,7 +326,7 @@ Rectangle { Separator { id: secondSeparator; - anchors.top: pttTextContainer.bottom; + anchors.top: pttTextContainer.visible ? pttTextContainer.bottom : switchesContainer.bottom; anchors.topMargin: 10; } @@ -341,7 +353,7 @@ Rectangle { width: margins.sizeText + margins.sizeLevel; anchors.left: parent.left; anchors.leftMargin: margins.sizeCheckBox; - size: 16; + size: 22; color: hifi.colors.white; text: qsTr("Choose input device"); } @@ -349,16 +361,17 @@ Rectangle { ListView { id: inputView; - width: parent.width - margins.paddings*2; + width: rightMostInputLevelPos; anchors.top: inputDeviceHeader.bottom; anchors.topMargin: 10; x: margins.paddings + interactive: false; height: contentHeight; spacing: 4; clip: true; model: AudioScriptingInterface.devices.input; delegate: Item { - width: rightMostInputLevelPos + width: rightMostInputLevelPos - margins.paddings*2 height: margins.sizeCheckBox > checkBoxInput.implicitHeight ? margins.sizeCheckBox : checkBoxInput.implicitHeight @@ -374,6 +387,7 @@ Rectangle { boxSize: margins.sizeCheckBox / 2 isRound: true text: devicename + fontSize: 16; onPressed: { if (!checked) { stereoInput.checked = false; @@ -407,7 +421,7 @@ Rectangle { Separator { id: thirdSeparator; - anchors.top: loopbackAudio.bottom; + anchors.top: loopbackAudio.visible ? loopbackAudio.bottom : inputView.bottom; anchors.topMargin: 10; } @@ -434,7 +448,7 @@ Rectangle { anchors.left: parent.left anchors.leftMargin: margins.sizeCheckBox anchors.verticalCenter: parent.verticalCenter; - size: 16; + size: 22; color: hifi.colors.white; text: qsTr("Choose output device"); } @@ -443,7 +457,8 @@ Rectangle { ListView { id: outputView width: parent.width - margins.paddings*2 - x: margins.paddings + x: margins.paddings; + interactive: false; height: contentHeight; anchors.top: outputDeviceHeader.bottom; anchors.topMargin: 10; @@ -464,6 +479,7 @@ Rectangle { checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; checkable: !checked text: devicename + fontSize: 16 onPressed: { if (!checked) { AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1); @@ -526,7 +542,7 @@ Rectangle { RalewayRegular { // The slider for my card is special, it controls the master gain id: avatarGainSliderText; - text: "Avatar volume"; + text: "People volume"; size: 16; anchors.left: parent.left; color: hifi.colors.white; diff --git a/interface/resources/qml/hifi/audio/InputPeak.qml b/interface/resources/qml/hifi/audio/InputPeak.qml index 00f7e63528..d8b166cee4 100644 --- a/interface/resources/qml/hifi/audio/InputPeak.qml +++ b/interface/resources/qml/hifi/audio/InputPeak.qml @@ -12,24 +12,26 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -Rectangle { +Item { property var peak; width: 70; height: 8; - color: "transparent"; - - Item { + QtObject { id: colors; + readonly property string unmuted: "#FFF"; readonly property string muted: "#E2334D"; readonly property string gutter: "#575757"; readonly property string greenStart: "#39A38F"; readonly property string greenEnd: "#1FC6A6"; + readonly property string yellow: "#C0C000"; readonly property string red: colors.muted; + readonly property string fill: "#55000000"; } + Text { id: status; @@ -79,23 +81,19 @@ Rectangle { anchors { fill: mask } source: mask start: Qt.point(0, 0); - end: Qt.point(70, 0); + end: Qt.point(bar.width, 0); gradient: Gradient { GradientStop { position: 0; color: colors.greenStart; } GradientStop { - position: 0.8; + position: 0.5; color: colors.greenEnd; } - GradientStop { - position: 0.801; - color: colors.red; - } GradientStop { position: 1; - color: colors.red; + color: colors.yellow; } } } diff --git a/interface/resources/qml/hifi/audio/LoopbackAudio.qml b/interface/resources/qml/hifi/audio/LoopbackAudio.qml index db0614dfdc..d3fd8ee075 100644 --- a/interface/resources/qml/hifi/audio/LoopbackAudio.qml +++ b/interface/resources/qml/hifi/audio/LoopbackAudio.qml @@ -60,11 +60,11 @@ RowLayout { } } -// RalewayRegular { -// Layout.leftMargin: 2; -// size: 14; -// color: "white"; -// font.italic: true -// text: audioLoopedBack ? qsTr("Speak in your input") : ""; -// } + RalewayRegular { + Layout.leftMargin: 2; + size: 18; + color: "white"; + font.italic: true + text: audioLoopedBack ? qsTr("Speak in your input") : ""; + } } diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index d161f12d70..55378589ec 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -16,13 +16,16 @@ import stylesUit 1.0 import TabletScriptingInterface 1.0 Rectangle { + id: micBar HifiConstants { id: hifi; } property var muted: AudioScriptingInterface.muted; readonly property var level: AudioScriptingInterface.inputLevel; + readonly property var clipping: AudioScriptingInterface.clipping; property var pushToTalk: AudioScriptingInterface.pushToTalk; property var pushingToTalk: AudioScriptingInterface.pushingToTalk; + readonly property var userSpeakingLevel: 0.4; property bool gated: false; Component.onCompleted: { AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; }); @@ -40,7 +43,6 @@ Rectangle { AudioScriptingInterface.pushingToTalkChanged.connect(function() { pushingToTalk = AudioScriptingInterface.pushingToTalk; }); - } property bool standalone: false; @@ -87,7 +89,7 @@ Rectangle { if (pushToTalk) { return; } - muted = !muted; + AudioScriptingInterface.muted = !muted; Tablet.playSound(TabletEnums.ButtonClick); } drag.target: dragTarget; @@ -130,9 +132,12 @@ Rectangle { readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg"; readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg"; readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg"; + readonly property string clippingIcon: "../../../icons/tablet-icons/mic-clip-i.svg"; + readonly property string gatedIcon: "../../../icons/tablet-icons/mic-gate-i.svg"; id: image; - source: (pushToTalk && !pushingToTalk) ? pushToTalkIcon : muted ? mutedIcon : unmutedIcon; + source: (pushToTalk && !pushingToTalk) ? pushToTalkIcon : muted ? mutedIcon : + clipping ? clippingIcon : gated ? gatedIcon : unmutedIcon; width: 30; height: 30; diff --git a/interface/resources/qml/hifi/audio/MicBarApplication.qml b/interface/resources/qml/hifi/audio/MicBarApplication.qml new file mode 100644 index 0000000000..bc3f4dff89 --- /dev/null +++ b/interface/resources/qml/hifi/audio/MicBarApplication.qml @@ -0,0 +1,255 @@ +// +// MicBarApplication.qml +// qml/hifi/audio +// +// Created by Zach Pomerantz on 6/14/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtGraphicalEffects 1.0 + +import stylesUit 1.0 +import TabletScriptingInterface 1.0 + +Rectangle { + id: micBar; + readonly property var level: AudioScriptingInterface.inputLevel; + readonly property var clipping: AudioScriptingInterface.clipping; + property var muted: AudioScriptingInterface.muted; + property var pushToTalk: AudioScriptingInterface.pushToTalk; + property var pushingToTalk: AudioScriptingInterface.pushingToTalk; + readonly property var userSpeakingLevel: 0.4; + property bool gated: false; + Component.onCompleted: { + AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; }); + AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; }); + HMD.displayModeChanged.connect(function() { + muted = AudioScriptingInterface.muted; + pushToTalk = AudioScriptingInterface.pushToTalk; + }); + AudioScriptingInterface.mutedChanged.connect(function() { + muted = AudioScriptingInterface.muted; + }); + AudioScriptingInterface.pushToTalkChanged.connect(function() { + pushToTalk = AudioScriptingInterface.pushToTalk; + }); + } + + readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg"; + readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg"; + readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg"; + readonly property string clippingIcon: "../../../icons/tablet-icons/mic-clip-i.svg"; + readonly property string gatedIcon: "../../../icons/tablet-icons/mic-gate-i.svg"; + property bool standalone: false; + property var dragTarget: null; + + width: 44; + height: 44; + + radius: 5; + opacity: 0.7; + + onLevelChanged: { + var rectOpacity = (muted && (level >= userSpeakingLevel)) ? 1.0 : 0.7; + if (pushToTalk && !pushingToTalk) { + rectOpacity = (mouseArea.containsMouse) ? 1.0 : 0.7; + } else if (mouseArea.containsMouse && rectOpacity != 1.0) { + rectOpacity = 1.0; + } + micBar.opacity = rectOpacity; + } + + color: "#00000000"; + border { + width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0; + color: colors.border; + } + + // borders are painted over fill, so reduce the fill to fit inside the border + Rectangle { + color: standalone ? colors.fill : "#00000000"; + width: 40; + height: 40; + + radius: 5; + + anchors { + verticalCenter: parent.verticalCenter; + horizontalCenter: parent.horizontalCenter; + } + } + + MouseArea { + id: mouseArea; + + anchors { + left: icon.left; + right: bar.right; + top: icon.top; + bottom: icon.bottom; + } + + hoverEnabled: true; + scrollGestureEnabled: false; + onClicked: { + if (pushToTalk) { + return; + } + AudioScriptingInterface.muted = !muted; + Tablet.playSound(TabletEnums.ButtonClick); + muted = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding + } + drag.target: dragTarget; + onContainsMouseChanged: { + if (containsMouse) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + } + + QtObject { + id: colors; + + readonly property string unmutedColor: "#FFF"; + readonly property string gatedColor: "#00BDFF"; + readonly property string mutedColor: "#E2334D"; + readonly property string gutter: "#575757"; + readonly property string greenStart: "#39A38F"; + readonly property string greenEnd: "#1FC6A6"; + readonly property string yellow: "#C0C000"; + readonly property string fill: "#55000000"; + readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF"; + readonly property string icon: (muted || clipping) ? mutedColor : gated ? gatedColor : unmutedColor; + } + + Item { + id: icon; + + anchors { + left: parent.left; + top: parent.top; + } + + width: 40; + height: 40; + + Item { + Image { + id: image; + source: (pushToTalk) ? pushToTalkIcon : muted ? mutedIcon : + clipping ? clippingIcon : gated ? gatedIcon : unmutedIcon; + width: 29; + height: 32; + anchors { + left: parent.left; + top: parent.top; + topMargin: 5; + } + } + + ColorOverlay { + id: imageOverlay + anchors { fill: image } + source: image; + color: pushToTalk ? (pushingToTalk ? colors.unmutedColor : colors.mutedColor) : colors.icon; + } + } + } + + Item { + id: status; + + visible: pushToTalk || (muted && (level >= userSpeakingLevel)); + + anchors { + left: parent.left; + top: icon.bottom; + topMargin: 2; + } + + width: parent.width; + height: statusTextMetrics.height; + + TextMetrics { + id: statusTextMetrics + text: statusText.text + font: statusText.font + } + + RalewaySemiBold { + id: statusText + anchors { + horizontalCenter: parent.horizontalCenter; + verticalCenter: parent.verticalCenter; + } + + color: pushToTalk ? (pushingToTalk ? colors.unmutedColor : colors.mutedColor) : (level >= userSpeakingLevel && muted) ? colors.mutedColor : colors.unmutedColor; + font.bold: true + + text: pushToTalk ? (HMD.active ? "PTT" : "PTT-(T)") : (muted ? "MUTED" : "MUTE"); + size: 12; + } + } + + Item { + id: bar; + + anchors { + right: parent.right; + rightMargin: 7; + top: parent.top + topMargin: 5 + } + + width: 8; + height: 32; + + Rectangle { // base + id: baseBar + radius: 4; + anchors { fill: parent } + color: colors.gutter; + } + + Rectangle { // mask + id: mask; + visible: (!(pushToTalk && !pushingToTalk)) + height: parent.height * level; + width: parent.width; + radius: 5; + anchors { + bottom: parent.bottom; + bottomMargin: 0; + left: parent.left; + leftMargin: 0; + } + } + + LinearGradient { + anchors { fill: mask } + visible: (!(pushToTalk && !pushingToTalk)) + source: mask + start: Qt.point(0, 0); + end: Qt.point(0, bar.height); + rotation: 180 + gradient: Gradient { + GradientStop { + position: 0.0; + color: colors.greenStart; + } + GradientStop { + position: 0.5; + color: colors.greenEnd; + } + GradientStop { + position: 1.0; + color: colors.yellow; + } + } + } + } +} diff --git a/interface/resources/qml/hifi/audio/PlaySampleSound.qml b/interface/resources/qml/hifi/audio/PlaySampleSound.qml index 033af99d04..f1351b8cbf 100644 --- a/interface/resources/qml/hifi/audio/PlaySampleSound.qml +++ b/interface/resources/qml/hifi/audio/PlaySampleSound.qml @@ -64,11 +64,11 @@ RowLayout { height: 32; } -// RalewayRegular { -// Layout.leftMargin: 2; -// size: 14; -// color: "white"; -// font.italic: true -// text: isPlaying ? qsTr("Listen to your output") : ""; -// } + RalewayRegular { + Layout.leftMargin: 2; + size: 18; + color: "white"; + font.italic: true + text: isPlaying ? qsTr("Listen to your output") : ""; + } } diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index a1da69a44a..1a1e0a96ff 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -40,7 +40,7 @@ Item { } } - HifiAudio.MicBar { + HifiAudio.MicBarApplication { anchors { left: parent.left leftMargin: 30 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b9c9a8f0a2..8225d27033 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -338,6 +338,10 @@ Setting::Handle maxOctreePacketsPerSecond{"maxOctreePPS", DEFAULT_MAX_OCTRE Setting::Handle loginDialogPoppedUp{"loginDialogPoppedUp", false}; +static const QUrl AVATAR_INPUTS_BAR_QML = PathUtils::qmlUrl("AvatarInputsBar.qml"); +static const QUrl MIC_BAR_APPLICATION_QML = PathUtils::qmlUrl("hifi/audio/MicBarApplication.qml"); +static const QUrl BUBBLE_ICON_QML = PathUtils::qmlUrl("BubbleIcon.qml"); + static const QString STANDARD_TO_ACTION_MAPPING_NAME = "Standard to Action"; static const QString NO_MOVEMENT_MAPPING_NAME = "Standard to Action (No Movement)"; static const QString NO_MOVEMENT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard_nomovement.json"; @@ -2372,7 +2376,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); }); auto rootItemLoadedFunctor = [webSurface, url, isTablet] { - Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == LOGIN_DIALOG.toString()); + Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == LOGIN_DIALOG.toString() || url == AVATAR_INPUTS_BAR_QML.toString() || + url == BUBBLE_ICON_QML.toString()); }; if (webSurface->getRootItem()) { rootItemLoadedFunctor(); @@ -3299,6 +3304,7 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { auto qml = PathUtils::qmlUrl("AvatarInputsBar.qml"); offscreenUi->show(qml, "AvatarInputsBar"); #endif + _desktopRootItemCreated = true; } void Application::userKickConfirmation(const QUuid& nodeID) { @@ -8980,6 +8986,38 @@ void Application::updateLoginDialogPosition() { } } +void Application::createAvatarInputsBar() { + const glm::vec3 LOCAL_POSITION { 0.0, 0.0, -1.0 }; + // DEFAULT_DPI / tablet scale percentage + const float DPI = 31.0f / (75.0f / 100.0f); + + EntityItemProperties properties; + properties.setType(EntityTypes::Web); + properties.setName("AvatarInputsBarEntity"); + properties.setSourceUrl(AVATAR_INPUTS_BAR_QML.toString()); + properties.setParentID(getMyAvatar()->getSelfID()); + properties.setParentJointIndex(getMyAvatar()->getJointIndex("_CAMERA_MATRIX")); + properties.setPosition(LOCAL_POSITION); + properties.setLocalRotation(Quaternions::IDENTITY); + //properties.setDimensions(LOGIN_DIMENSIONS); + properties.setPrimitiveMode(PrimitiveMode::SOLID); + properties.getGrab().setGrabbable(false); + properties.setIgnorePickIntersection(false); + properties.setAlpha(1.0f); + properties.setDPI(DPI); + properties.setVisible(true); + + auto entityScriptingInterface = DependencyManager::get(); + _avatarInputsBarID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); +} + +void Application::destroyAvatarInputsBar() { + auto entityScriptingInterface = DependencyManager::get(); + if (!_avatarInputsBarID.isNull()) { + entityScriptingInterface->deleteEntity(_avatarInputsBarID); + } +} + bool Application::hasRiftControllers() { return PluginUtils::isOculusTouchControllerAvailable(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 762ac9585a..99e57f1866 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -330,6 +330,9 @@ public: void createLoginDialog(); void updateLoginDialogPosition(); + void createAvatarInputsBar(); + void destroyAvatarInputsBar(); + // Check if a headset is connected bool hasRiftControllers(); bool hasViveControllers(); @@ -704,12 +707,14 @@ private: int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS; bool _interstitialModeEnabled{ false }; - bool _loginDialogPoppedUp = false; + bool _loginDialogPoppedUp{ false }; + bool _desktopRootItemCreated{ false }; bool _developerMenuVisible{ false }; QString _previousAvatarSkeletonModel; float _previousAvatarTargetScale; CameraMode _previousCameraMode; QUuid _loginDialogID; + QUuid _avatarInputsBarID; LoginStateManager _loginStateManager; quint64 _lastFaceTrackerUpdate; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 69f7054953..76612039db 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "Application.h" #include "InterfaceLogging.h" @@ -536,6 +537,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar avatar->removeAvatarEntitiesFromTree(); if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { + emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID()); emit DependencyManager::get()->enteredIgnoreRadius(); } else if (removalReason == KillAvatarReason::AvatarDisconnected) { // remove from node sets, if present diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index ae0294d763..caae946116 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -88,44 +88,44 @@ void Audio::setMuted(bool isMuted) { void Audio::setMutedDesktop(bool isMuted) { bool changed = false; withWriteLock([&] { - if (_desktopMuted != isMuted) { + if (_mutedDesktop != isMuted) { changed = true; - _desktopMuted = isMuted; + _mutedDesktop = isMuted; auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); } }); if (changed) { emit mutedChanged(isMuted); - emit desktopMutedChanged(isMuted); + emit mutedDesktopChanged(isMuted); } } bool Audio::getMutedDesktop() const { return resultWithReadLock([&] { - return _desktopMuted; + return _mutedDesktop; }); } void Audio::setMutedHMD(bool isMuted) { bool changed = false; withWriteLock([&] { - if (_hmdMuted != isMuted) { + if (_mutedHMD != isMuted) { changed = true; - _hmdMuted = isMuted; + _mutedHMD = isMuted; auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); } }); if (changed) { emit mutedChanged(isMuted); - emit hmdMutedChanged(isMuted); + emit mutedHMDChanged(isMuted); } } bool Audio::getMutedHMD() const { return resultWithReadLock([&] { - return _hmdMuted; + return _mutedHMD; }); } @@ -217,15 +217,15 @@ void Audio::setPTTHMD(bool enabled) { } void Audio::saveData() { - _desktopMutedSetting.set(getMutedDesktop()); - _hmdMutedSetting.set(getMutedHMD()); + _mutedDesktopSetting.set(getMutedDesktop()); + _mutedHMDSetting.set(getMutedHMD()); _pttDesktopSetting.set(getPTTDesktop()); _pttHMDSetting.set(getPTTHMD()); } void Audio::loadData() { - setMutedDesktop(_desktopMutedSetting.get()); - setMutedHMD(_hmdMutedSetting.get()); + setMutedDesktop(_mutedDesktopSetting.get()); + setMutedHMD(_mutedHMDSetting.get()); setPTTDesktop(_pttDesktopSetting.get()); setPTTHMD(_pttHMDSetting.get()); diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index d6823ea452..00da566b30 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -41,6 +41,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { * @hifi-assignment-client * * @property {boolean} muted - true if the audio input is muted, otherwise false. + * @property {boolean} mutedDesktop - true if the audio input is muted, otherwise false. * @property {boolean} noiseReduction - true if noise reduction is enabled, otherwise false. When * enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just * above the noise floor. @@ -68,8 +69,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged) Q_PROPERTY(QString context READ getContext NOTIFY contextChanged) Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop) - Q_PROPERTY(bool desktopMuted READ getMutedDesktop WRITE setMutedDesktop NOTIFY desktopMutedChanged) - Q_PROPERTY(bool hmdMuted READ getMutedHMD WRITE setMutedHMD NOTIFY hmdMutedChanged) + Q_PROPERTY(bool mutedDesktop READ getMutedDesktop WRITE setMutedDesktop NOTIFY mutedDesktopChanged) + Q_PROPERTY(bool mutedHMD READ getMutedHMD WRITE setMutedHMD NOTIFY mutedHMDChanged) Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged); Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged) Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged) @@ -287,19 +288,19 @@ signals: /**jsdoc * Triggered when desktop audio input is muted or unmuted. - * @function Audio.desktopMutedChanged + * @function Audio.mutedDesktopChanged * @param {boolean} isMuted - true if the audio input is muted for desktop mode, otherwise false. * @returns {Signal} */ - void desktopMutedChanged(bool isMuted); + void mutedDesktopChanged(bool isMuted); /**jsdoc * Triggered when HMD audio input is muted or unmuted. - * @function Audio.hmdMutedChanged + * @function Audio.mutedHMDChanged * @param {boolean} isMuted - true if the audio input is muted for HMD mode, otherwise false. * @returns {Signal} */ - void hmdMutedChanged(bool isMuted); + void mutedHMDChanged(bool isMuted); /** * Triggered when Push-to-Talk has been enabled or disabled. @@ -418,12 +419,12 @@ private: bool _contextIsHMD { false }; AudioDevices* getDevices() { return &_devices; } AudioDevices _devices; - Setting::Handle _desktopMutedSetting{ QStringList { Audio::AUDIO, "desktopMuted" }, true }; - Setting::Handle _hmdMutedSetting{ QStringList { Audio::AUDIO, "hmdMuted" }, true }; + Setting::Handle _mutedDesktopSetting{ QStringList { Audio::AUDIO, "mutedDesktop" }, true }; + Setting::Handle _mutedHMDSetting{ QStringList { Audio::AUDIO, "mutedHMD" }, true }; Setting::Handle _pttDesktopSetting{ QStringList { Audio::AUDIO, "pushToTalkDesktop" }, false }; Setting::Handle _pttHMDSetting{ QStringList { Audio::AUDIO, "pushToTalkHMD" }, false }; - bool _desktopMuted{ true }; - bool _hmdMuted{ false }; + bool _mutedDesktop{ true }; + bool _mutedHMD{ false }; bool _pttDesktop{ false }; bool _pttHMD{ false }; bool _pushingToTalk{ false }; diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 0aa352de23..80604f354b 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "Application.h" #include "Menu.h" @@ -30,6 +31,10 @@ AvatarInputs* AvatarInputs::getInstance() { AvatarInputs::AvatarInputs(QObject* parent) : QObject(parent) { _showAudioTools = showAudioToolsSetting.get(); + auto nodeList = DependencyManager::get(); + auto usersScriptingInterface = DependencyManager::get(); + connect(nodeList.data(), &NodeList::ignoreRadiusEnabledChanged, this, &AvatarInputs::ignoreRadiusEnabledChanged); + connect(usersScriptingInterface.data(), &UsersScriptingInterface::enteredIgnoreRadius, this, &AvatarInputs::enteredIgnoreRadiusChanged); } #define AI_UPDATE(name, src) \ @@ -83,6 +88,10 @@ void AvatarInputs::setShowAudioTools(bool showAudioTools) { emit showAudioToolsChanged(_showAudioTools); } +bool AvatarInputs::getIgnoreRadiusEnabled() const { + return DependencyManager::get()->getIgnoreRadiusEnabled(); +} + void AvatarInputs::toggleCameraMute() { FaceTracker* faceTracker = qApp->getSelectedFaceTracker(); if (faceTracker) { diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index 6569792807..f53adc1749 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -42,6 +42,8 @@ class AvatarInputs : public QObject { AI_PROPERTY(bool, isHMD, false) Q_PROPERTY(bool showAudioTools READ showAudioTools WRITE setShowAudioTools NOTIFY showAudioToolsChanged) + Q_PROPERTY(bool ignoreRadiusEnabled READ getIgnoreRadiusEnabled NOTIFY ignoreRadiusEnabledChanged) + //Q_PROPERTY(bool enteredIgnoreRadius READ getEnteredIgnoreRadius NOTIFY enteredIgnoreRadiusChanged) public: static AvatarInputs* getInstance(); @@ -55,7 +57,9 @@ public: AvatarInputs(QObject* parent = nullptr); void update(); - bool showAudioTools() const { return _showAudioTools; } + bool showAudioTools() const { return _showAudioTools; } + bool getIgnoreRadiusEnabled() const; + //bool getEnteredIgnoreRadius() const; public slots: @@ -93,6 +97,34 @@ signals: */ void showAudioToolsChanged(bool show); + /**jsdoc + * @function AvatarInputs.avatarEnteredIgnoreRadius + * @param {QUuid} avatarID + * @returns {Signal} + */ + void avatarEnteredIgnoreRadius(QUuid avatarID); + + /**jsdoc + * @function AvatarInputs.avatarLeftIgnoreRadius + * @param {QUuid} avatarID + * @returns {Signal} + */ + void avatarLeftIgnoreRadius(QUuid avatarID); + + /**jsdoc + * @function AvatarInputs.ignoreRadiusEnabledChanged + * @param {boolean} enabled + * @returns {Signal} + */ + void ignoreRadiusEnabledChanged(bool enabled); + + /**jsdoc + * @function AvatarInputs.enteredIgnoreRadiusChanged + * @param {boolean} enabled + * @returns {Signal} + */ + void enteredIgnoreRadiusChanged(); + protected: /**jsdoc @@ -106,6 +138,8 @@ protected: Q_INVOKABLE void toggleCameraMute(); private: + void onAvatarEnteredIgnoreRadius(); + void onAvatarLeftIgnoreRadius(); float _trailingAudioLoudness{ 0 }; bool _showAudioTools { false }; }; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index caa1f9f892..1c4b0cfc53 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1707,6 +1707,7 @@ protected: glm::vec3 _globalBoundingBoxOffset; AABox _defaultBubbleBox; + AABox _fitBoundingBox; mutable ReadWriteLockable _avatarEntitiesLock; AvatarEntityIDs _avatarEntityRemoved; // recently removed AvatarEntity ids diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index c63170de75..2f47ef5e00 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -71,12 +71,12 @@ void UserActivityLoggerScriptingInterface::makeUserConnection(QString otherID, b doLogAction("makeUserConnection", payload); } -void UserActivityLoggerScriptingInterface::bubbleToggled(bool newValue) { - doLogAction(newValue ? "bubbleOn" : "bubbleOff"); +void UserActivityLoggerScriptingInterface::privacyShieldToggled(bool newValue) { + doLogAction(newValue ? "privacyShieldOn" : "privacyShieldOff"); } -void UserActivityLoggerScriptingInterface::bubbleActivated() { - doLogAction("bubbleActivated"); +void UserActivityLoggerScriptingInterface::privacyShieldActivated() { + doLogAction("privacyShieldActivated"); } void UserActivityLoggerScriptingInterface::logAction(QString action, QVariantMap details) { diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 71d411056d..1cda1235e9 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -30,8 +30,8 @@ public: Q_INVOKABLE void palAction(QString action, QString target); Q_INVOKABLE void palOpened(float secondsOpen); Q_INVOKABLE void makeUserConnection(QString otherUser, bool success, QString details = ""); - Q_INVOKABLE void bubbleToggled(bool newValue); - Q_INVOKABLE void bubbleActivated(); + Q_INVOKABLE void privacyShieldToggled(bool newValue); + Q_INVOKABLE void privacyShieldActivated(); Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{}); Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem); Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem, QString errorDetails); diff --git a/scripts/developer/createAvatarInputsBarEntity.js b/scripts/developer/createAvatarInputsBarEntity.js new file mode 100644 index 0000000000..deb0cfdd89 --- /dev/null +++ b/scripts/developer/createAvatarInputsBarEntity.js @@ -0,0 +1,138 @@ +"use strict"; + +(function(){ + var AppUi = Script.require("appUi"); + + var ui; + + var onCreateAvatarInputsBarEntity = false; + var micBarEntity = null; + var bubbleIconEntity = null; + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var AVATAR_INPUTS_EDIT_QML_SOURCE = "hifi/EditAvatarInputsBar.qml"; + + // DPI + var ENTITY_DPI = 60.0; + // QML NATURAL DIMENSIONS + var MIC_BAR_DIMENSIONS = Vec3.multiply(30.0 / ENTITY_DPI, {x: 0.036, y: 0.048, z: 0.3}); + var BUBBLE_ICON_DIMENSIONS = Vec3.multiply(30.0 / ENTITY_DPI, {x: 0.036, y: 0.036, z: 0.3}); + // ENTITY NAMES + var MIC_BAR_NAME = "AvatarInputsMicBarEntity"; + var BUBBLE_ICON_NAME = "AvatarInputsBubbleIconEntity"; + // CONSTANTS + var LOCAL_POSITION_X_OFFSET = -0.2; + var LOCAL_POSITION_Y_OFFSET = -0.125; + var LOCAL_POSITION_Z_OFFSET = -0.5; + + function fromQml(message) { + if (message.method === "reposition") { + var micBarLocalPosition = Entities.getEntityProperties(micBarEntity).localPosition; + var bubbleIconLocalPosition = Entities.getEntityProperties(bubbleIconEntity).localPosition; + var newMicBarLocalPosition, newBubbleIconLocalPosition; + if (message.x !== undefined) { + newMicBarLocalPosition = { x: -((MIC_BAR_DIMENSIONS.x) / 2) + message.x, y: micBarLocalPosition.y, z: micBarLocalPosition.z }; + newBubbleIconLocalPosition = { x: ((MIC_BAR_DIMENSIONS.x) * 1.2 / 2) + message.x, y: bubbleIconLocalPosition.y, z: bubbleIconLocalPosition.z }; + } else if (message.y !== undefined) { + newMicBarLocalPosition = { x: micBarLocalPosition.x, y: message.y, z: micBarLocalPosition.z }; + newBubbleIconLocalPosition = { x: bubbleIconLocalPosition.x, y: ((MIC_BAR_DIMENSIONS.y - BUBBLE_ICON_DIMENSIONS.y) / 2 + message.y), z: bubbleIconLocalPosition.z }; + } else if (message.z !== undefined) { + newMicBarLocalPosition = { x: micBarLocalPosition.x, y: micBarLocalPosition.y, z: message.z }; + newBubbleIconLocalPosition = { x: bubbleIconLocalPosition.x, y: bubbleIconLocalPosition.y, z: message.z }; + } + var micBarProps = { + localPosition: newMicBarLocalPosition + }; + var bubbleIconProps = { + localPosition: newBubbleIconLocalPosition + }; + + Entities.editEntity(micBarEntity, micBarProps); + Entities.editEntity(bubbleIconEntity, bubbleIconProps); + } else if (message.method === "setVisible") { + if (message.visible !== undefined) { + var props = { + visible: message.visible + }; + Entities.editEntity(micBarEntity, props); + Entities.editEntity(bubbleIconEntity, props); + } + } else if (message.method === "print") { + // prints the local position into the hifi log. + var micBarLocalPosition = Entities.getEntityProperties(micBarEntity).localPosition; + var bubbleIconLocalPosition = Entities.getEntityProperties(bubbleIconEntity).localPosition; + console.log("mic bar local position is at " + JSON.stringify(micBarLocalPosition)); + console.log("bubble icon local position is at " + JSON.stringify(bubbleIconLocalPosition)); + } + }; + + function createEntities() { + if (micBarEntity != null && bubbleIconEntity != null) { + return; + } + // POSITIONS + var micBarLocalPosition = {x: (-(MIC_BAR_DIMENSIONS.x / 2)) + LOCAL_POSITION_X_OFFSET, y: LOCAL_POSITION_Y_OFFSET, z: LOCAL_POSITION_Z_OFFSET}; + var bubbleIconLocalPosition = {x: (MIC_BAR_DIMENSIONS.x * 1.2 / 2) + LOCAL_POSITION_X_OFFSET, y: ((MIC_BAR_DIMENSIONS.y - BUBBLE_ICON_DIMENSIONS.y) / 2 + LOCAL_POSITION_Y_OFFSET), z: LOCAL_POSITION_Z_OFFSET}; + var props = { + type: "Web", + name: MIC_BAR_NAME, + parentID: MyAvatar.SELF_ID, + parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX"), + localPosition: micBarLocalPosition, + localRotation: Quat.cancelOutRollAndPitch(Quat.lookAtSimple(Camera.orientation, micBarLocalPosition)), + sourceUrl: Script.resourcesPath() + "qml/hifi/audio/MicBarApplication.qml", + // cutoff alpha for detecting transparency + alpha: 0.98, + dimensions: MIC_BAR_DIMENSIONS, + dpi: ENTITY_DPI, + drawInFront: true, + userData: { + grabbable: false + }, + }; + micBarEntity = Entities.addEntity(props, "local"); + var props = { + type: "Web", + name: BUBBLE_ICON_NAME, + parentID: MyAvatar.SELF_ID, + parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX"), + localPosition: bubbleIconLocalPosition, + localRotation: Quat.cancelOutRollAndPitch(Quat.lookAtSimple(Camera.orientation, bubbleIconLocalPosition)), + sourceUrl: Script.resourcesPath() + "qml/BubbleIcon.qml", + // cutoff alpha for detecting transparency + alpha: 0.98, + dimensions: BUBBLE_ICON_DIMENSIONS, + dpi: ENTITY_DPI, + drawInFront: true, + userData: { + grabbable: false + }, + }; + bubbleIconEntity = Entities.addEntity(props, "local"); + tablet.loadQMLSource(AVATAR_INPUTS_EDIT_QML_SOURCE); + }; + function cleanup() { + if (micBarEntity) { + Entities.deleteEntity(micBarEntity); + } + if (bubbleIconEntity) { + Entities.deleteEntity(bubbleIconEntity); + } + }; + + function setup() { + ui = new AppUi({ + buttonName: "AVBAR", + home: Script.resourcesPath() + "qml/hifi/EditAvatarInputsBar.qml", + onMessage: fromQml, + onOpened: createEntities, + // onClosed: cleanup, + normalButton: "icons/tablet-icons/edit-i.svg", + activeButton: "icons/tablet-icons/edit-a.svg", + }); + }; + + setup(); + + Script.scriptEnding.connect(cleanup); + +}()); diff --git a/scripts/system/audio.js b/scripts/system/audio.js index 19ed3faef2..a161b40ffd 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -75,6 +75,7 @@ button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); Audio.mutedChanged.connect(onMuteToggled); Audio.pushToTalkChanged.connect(onMuteToggled); +HMD.displayModeChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -84,6 +85,7 @@ Script.scriptEnding.connect(function () { tablet.screenChanged.disconnect(onScreenChanged); Audio.mutedChanged.disconnect(onMuteToggled); Audio.pushToTalkChanged.disconnect(onMuteToggled); + HMD.displayModeChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index e715e97575..9acc5ab123 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -43,7 +43,7 @@ if (HMD.active) { warningOverlayID = Overlays.addOverlay("text3d", { name: "Muted-Warning", - localPosition: { x: 0.0, y: -0.5, z: -1.0 }, + localPosition: { x: 0.0, y: -0.45, z: -1.0 }, localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 0.0, z: 0.0, w: 1.0 }), text: warningText, textAlpha: 1, @@ -58,20 +58,6 @@ parentID: MyAvatar.SELF_ID, parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX") }); - } else { - var textDimensions = { x: 100, y: 50 }; - warningOverlayID = Overlays.addOverlay("text", { - name: "Muted-Warning", - font: { size: 36 }, - text: warningText, - x: (Window.innerWidth - textDimensions.x) / 2, - y: (Window.innerHeight - textDimensions.y), - width: textDimensions.x, - height: textDimensions.y, - textColor: { red: 226, green: 51, blue: 77 }, - backgroundAlpha: 0, - visible: true - }); } } @@ -141,4 +127,4 @@ Audio.mutedChanged.connect(startOrStopPoll); Audio.warnWhenMutedChanged.connect(startOrStopPoll); -}()); // END LOCAL_SCOPE \ No newline at end of file +}()); // END LOCAL_SCOPE diff --git a/scripts/system/away.js b/scripts/system/away.js index 2af43b2055..e45041139a 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -171,7 +171,7 @@ function goAway(fromStartup) { if (!previousBubbleState) { Users.toggleIgnoreRadius(); } - UserActivityLogger.bubbleToggled(Users.getIgnoreRadiusEnabled()); + UserActivityLogger.privacyShieldToggled(Users.getIgnoreRadiusEnabled()); UserActivityLogger.toggledAway(true); MyAvatar.isAway = true; } @@ -186,7 +186,7 @@ function goActive() { if (Users.getIgnoreRadiusEnabled() !== previousBubbleState) { Users.toggleIgnoreRadius(); - UserActivityLogger.bubbleToggled(Users.getIgnoreRadiusEnabled()); + UserActivityLogger.privacyShieldToggled(Users.getIgnoreRadiusEnabled()); } if (!Window.hasFocus()) { diff --git a/scripts/system/bubble.js b/scripts/system/bubble.js index 6ca624872e..eca3b3dcd4 100644 --- a/scripts/system/bubble.js +++ b/scripts/system/bubble.js @@ -90,7 +90,7 @@ // Called from the C++ scripting interface to show the bubble overlay function enteredIgnoreRadius() { createOverlays(); - UserActivityLogger.bubbleActivated(); + UserActivityLogger.privacyShieldActivated(); } // Used to set the state of the bubble HUD button @@ -160,7 +160,7 @@ function onBubbleToggled(enabled, doNotLog) { writeButtonProperties(enabled); if (doNotLog !== true) { - UserActivityLogger.bubbleToggled(enabled); + UserActivityLogger.privacyShieldToggled(enabled); } if (enabled) { createOverlays(); @@ -174,7 +174,7 @@ } // Setup the bubble button - var buttonName = "BUBBLE"; + var buttonName = "SHIELD"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); button = tablet.addButton({ icon: "icons/tablet-icons/bubble-i.svg",