diff --git a/interface/resources/icons/tablet-icons/mic-mute.svg b/interface/resources/icons/tablet-icons/mic-mute.svg new file mode 100644 index 0000000000..bd42fded05 --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic-mute.svg @@ -0,0 +1,60 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/interface/resources/icons/tablet-icons/mic.svg b/interface/resources/icons/tablet-icons/mic.svg new file mode 100644 index 0000000000..30b46d18dd --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic.svg @@ -0,0 +1,62 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml index 0d563ed5bb..3fb70f9cca 100644 --- a/interface/resources/qml/hifi/tablet/Tablet.qml +++ b/interface/resources/qml/hifi/tablet/Tablet.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.5 import QtGraphicalEffects 1.0 import "../../styles-uit" @@ -6,10 +6,16 @@ Item { id: tablet objectName: "tablet" property double micLevel: 0.8 + property bool micEnabled: true property int rowIndex: 0 property int columnIndex: 0 property int count: (flowMain.children.length - 1) + // called by C++ code to keep mic state updated + function setMicEnabled(newMicEnabled) { + tablet.micEnabled = newMicEnabled; + } + // called by C++ code to keep audio bar updated function setMicLevel(newMicLevel) { tablet.micLevel = newMicLevel; @@ -97,17 +103,38 @@ Item { anchors.topMargin: 0 anchors.top: parent.top - - Image { - id: muteIcon + Item { + id: audioIcon + anchors.verticalCenter: parent.verticalCenter width: 40 height: 40 - source: "../../../icons/tablet-mute-icon.svg" - anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 5 + + Image { + id: micIcon + source: "../../../icons/tablet-icons/mic.svg" + } + + Item { + visible: (!tablet.micEnabled && !toggleMuteMouseArea.containsMouse) + || (tablet.micEnabled && toggleMuteMouseArea.containsMouse) + + Image { + id: muteIcon + source: "../../../icons/tablet-icons/mic-mute.svg" + } + + ColorOverlay { + anchors.fill: muteIcon + source: muteIcon + color: toggleMuteMouseArea.containsMouse ? "#a0a0a0" : "#ff0000" + } + } } Item { - id: item1 + id: audioBar width: 170 height: 10 anchors.left: parent.left @@ -157,6 +184,22 @@ Item { } } + MouseArea { + id: toggleMuteMouseArea + anchors { + left: audioIcon.left + right: audioBar.right + top: audioIcon.top + bottom: audioIcon.bottom + } + + hoverEnabled: true + preventStealing: true + propagateComposedEvents: false + scrollGestureEnabled: false + onClicked: tabletRoot.toggleMicEnabled() + } + RalewaySemiBold { id: usernameText text: tablet.parent.parent.username @@ -175,7 +218,6 @@ Item { GradientStop { position: 0 color: "#2b2b2b" - } GradientStop { @@ -225,7 +267,7 @@ Item { PropertyChanges { target: muteIcon - source: "../../../icons/tablet-unmute-icon.svg" + visible: micEnabled } PropertyChanges { diff --git a/interface/resources/qml/hifi/tablet/TabletMenu.qml b/interface/resources/qml/hifi/tablet/TabletMenu.qml index cd3a9cacae..c154ac0f49 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenu.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenu.qml @@ -14,6 +14,7 @@ FocusScope { property var rootMenu: Menu { objectName:"rootMenu" } property var point: Qt.point(50, 50) + property string subMenu: "" TabletMouseHandler { id: menuPopperUpper } @@ -101,6 +102,24 @@ FocusScope { buildMenu() } function buildMenu() { - menuPopperUpper.popup(tabletMenu, rootMenu.items) + // Build submenu if specified. + if (subMenu !== "") { + var index = 0; + var found = false; + while (!found && index < rootMenu.items.length) { + found = rootMenu.items[index].title === subMenu; + if (!found) { + index += 1; + } + } + subMenu = ""; // Continue with full menu after initially displaying submenu. + if (found) { + menuPopperUpper.popup(tabletMenu, rootMenu.items[index].items); + return; + } + } + + // Otherwise build whole menu. + menuPopperUpper.popup(tabletMenu, rootMenu.items); } } diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 0260bd6a01..481c7846a9 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -6,9 +6,14 @@ Item { objectName: "tabletRoot" property string username: "Unknown user" property var eventBridge; + property string option: "" signal showDesktop(); + function setOption(value) { + option = value; + } + function loadSource(url) { loader.source = url; } @@ -42,6 +47,10 @@ Item { } } + function toggleMicEnabled() { + ApplicationInterface.toggleMuteAudio(); + } + function setUsername(newUsername) { username = newUsername; } @@ -68,6 +77,9 @@ Item { if (loader.item.hasOwnProperty("sendToScript")) { loader.item.sendToScript.connect(tabletRoot.sendToScript); } + if (loader.item.hasOwnProperty("subMenu")) { + loader.item.subMenu = option; + } loader.item.forceActiveFocus(); } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b7aecfa597..488e97b5e6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6853,3 +6853,8 @@ void Application::updateThreadPoolCount() const { qCDebug(interfaceapp) << "Setting thread pool size to " << threadPoolSize; QThreadPool::globalInstance()->setMaxThreadCount(threadPoolSize); } + +void Application::toggleMuteAudio() { + auto menu = Menu::getInstance(); + menu->setIsOptionChecked(MenuOption::MuteAudio, !menu->isOptionChecked(MenuOption::MuteAudio)); +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 6072acac93..cab830ec88 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -386,6 +386,8 @@ public slots: void addAssetToWorldMessageClose(); + Q_INVOKABLE void toggleMuteAudio(); + private slots: void showDesktop(); void clearDomainOctreeDetails(); diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 71f0073ead..7e8fdd6bc3 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -213,10 +213,11 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr } } -void TabletProxy::gotoMenuScreen() { +void TabletProxy::gotoMenuScreen(const QString& submenu) { if (_qmlTabletRoot) { if (_state != State::Menu) { removeButtonsFromHomeScreen(); + QMetaObject::invokeMethod(_qmlTabletRoot, "setOption", Q_ARG(const QVariant&, QVariant(submenu))); auto loader = _qmlTabletRoot->findChild("loader"); QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen()), Qt::DirectConnection); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL))); @@ -309,6 +310,15 @@ void TabletProxy::removeButton(QObject* tabletButtonProxy) { } } +void TabletProxy::updateMicEnabled(const bool micOn) { + auto tablet = getQmlTablet(); + if (!tablet) { + //qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; + } else { + QMetaObject::invokeMethod(tablet, "setMicEnabled", Qt::AutoConnection, Q_ARG(QVariant, QVariant(micOn))); + } +} + void TabletProxy::updateAudioBar(const double micLevel) { auto tablet = getQmlTablet(); if (!tablet) { @@ -359,13 +369,13 @@ void TabletProxy::addButtonsToMenuScreen() { } QQuickItem* VrMenu = loader->findChild("tabletMenu"); - if (!VrMenu) { - return; + if (VrMenu) { + auto offscreenUi = DependencyManager::get(); + QObject* menu = offscreenUi->getRootMenu(); + QMetaObject::invokeMethod(VrMenu, "setRootMenu", Qt::AutoConnection, Q_ARG(QVariant, QVariant::fromValue(menu))); } - auto offscreenUi = DependencyManager::get(); - QObject* menu = offscreenUi->getRootMenu(); - QMetaObject::invokeMethod(VrMenu, "setRootMenu", Qt::AutoConnection, Q_ARG(QVariant, QVariant::fromValue(menu))); + QObject::disconnect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen())); } void TabletProxy::removeButtonsFromHomeScreen() { diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index 9986fc78b0..8ba69ccdde 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -72,7 +72,7 @@ public: void setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface); - Q_INVOKABLE void gotoMenuScreen(); + Q_INVOKABLE void gotoMenuScreen(const QString& submenu = ""); /**jsdoc * transition to the home screen @@ -106,6 +106,13 @@ public: */ Q_INVOKABLE void removeButton(QObject* tabletButtonProxy); + /**jsdoc + * Updates the tablet's mic enabled state + * @function TabletProxy#updateMicEnabled + * @param micEnabled {bool} mic enabled state + */ + Q_INVOKABLE void updateMicEnabled(const bool micEnabled); + /**jsdoc * Updates the audio bar in tablet to reflect latest mic level * @function TabletProxy#updateAudioBar diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 36b0ddde85..40a77eda55 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -14,7 +14,7 @@ var DEFAULT_SCRIPTS = [ "system/progress.js", "system/away.js", - "system/mute.js", + "system/audio.js", "system/hmd.js", "system/menu.js", "system/bubble.js", diff --git a/scripts/system/mute.js b/scripts/system/audio.js similarity index 56% rename from scripts/system/mute.js rename to scripts/system/audio.js index 822a8f127a..dd49f944ea 100644 --- a/scripts/system/mute.js +++ b/scripts/system/audio.js @@ -1,8 +1,7 @@ "use strict"; // -// goto.js -// scripts/system/ +// audio.js // // Created by Howard Stearns on 2 Jun 2016 // Copyright 2016 High Fidelity, Inc. @@ -14,22 +13,33 @@ (function() { // BEGIN LOCAL_SCOPE var button; -var buttonName = "MUTE"; +var TOOLBAR_BUTTON_NAME = "MUTE"; +var TABLET_BUTTON_NAME = "AUDIO"; var toolBar = null; var tablet = null; +var isHUDUIEnabled = Settings.getValue("HUDUIEnabled"); +var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; function onMuteToggled() { - button.editProperties({isActive: AudioDevice.getMuted()}); + if (isHUDUIEnabled) { + button.editProperties({ isActive: AudioDevice.getMuted() }); + } } function onClicked(){ - var menuItem = "Mute Microphone"; - Menu.setIsOptionChecked(menuItem, !Menu.isOptionChecked(menuItem)); + if (isHUDUIEnabled) { + var menuItem = "Mute Microphone"; + Menu.setIsOptionChecked(menuItem, !Menu.isOptionChecked(menuItem)); + } else { + var entity = HMD.tabletID; + Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + tablet.gotoMenuScreen("Audio"); + } } if (Settings.getValue("HUDUIEnabled")) { toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); button = toolBar.addButton({ - objectName: buttonName, + objectName: TOOLBAR_BUTTON_NAME, imageURL: Script.resolvePath("assets/images/tools/mic.svg"), visible: true, alpha: 0.9 @@ -37,12 +47,8 @@ if (Settings.getValue("HUDUIEnabled")) { } else { tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); button = tablet.addButton({ - icon: "icons/tablet-icons/mic-unmute-i.svg", - hoverIcon: "icons/tablet-icons/mic-mute-i.svg", - activeIcon: "icons/tablet-icons/mic-mute-a.svg", - activeHoverIcon: "icons/tablet-icons/mic-unmute-a.svg", - text: "MUTE", - activeText: "UNMUTE", + icon: "icons/tablet-icons/mic-i.svg", + text: TABLET_BUTTON_NAME, sortOrder: 1 }); } @@ -58,7 +64,7 @@ Script.scriptEnding.connect(function () { tablet.removeButton(button); } if (toolBar) { - toolBar.removeButton(buttonName); + toolBar.removeButton(TOOLBAR_BUTTON_NAME); } }); diff --git a/scripts/system/selectAudioDevice.js b/scripts/system/selectAudioDevice.js index c618909e8c..9b97b24455 100644 --- a/scripts/system/selectAudioDevice.js +++ b/scripts/system/selectAudioDevice.js @@ -52,33 +52,7 @@ var selectedInputMenu = ""; var selectedOutputMenu = ""; function setupAudioMenus() { - Menu.addMenu("Audio > Devices"); - Menu.addSeparator("Audio > Devices","Output Audio Device"); - - var outputDeviceSetting = Settings.getValue(OUTPUT_DEVICE_SETTING); - var outputDevices = AudioDevice.getOutputDevices(); - var selectedOutputDevice = AudioDevice.getOutputDevice(); - if (outputDevices.indexOf(outputDeviceSetting) != -1 && selectedOutputDevice != outputDeviceSetting) { - if (AudioDevice.setOutputDevice(outputDeviceSetting)) { - selectedOutputDevice = outputDeviceSetting; - } - } - print("audio output devices: " + outputDevices); - for(var i = 0; i < outputDevices.length; i++) { - var thisDeviceSelected = (outputDevices[i] == selectedOutputDevice); - var menuItem = "Use " + outputDevices[i] + " for Output"; - Menu.addMenuItem({ - menuName: "Audio > Devices", - menuItemName: menuItem, - isCheckable: true, - isChecked: thisDeviceSelected - }); - if (thisDeviceSelected) { - selectedOutputMenu = menuItem; - } - } - - Menu.addSeparator("Audio > Devices","Input Audio Device"); + Menu.addSeparator("Audio", "Input Audio Device"); var inputDeviceSetting = Settings.getValue(INPUT_DEVICE_SETTING); var inputDevices = AudioDevice.getInputDevices(); @@ -93,7 +67,7 @@ function setupAudioMenus() { var thisDeviceSelected = (inputDevices[i] == selectedInputDevice); var menuItem = "Use " + inputDevices[i] + " for Input"; Menu.addMenuItem({ - menuName: "Audio > Devices", + menuName: "Audio", menuItemName: menuItem, isCheckable: true, isChecked: thisDeviceSelected @@ -102,6 +76,31 @@ function setupAudioMenus() { selectedInputMenu = menuItem; } } + + Menu.addSeparator("Audio", "Output Audio Device"); + + var outputDeviceSetting = Settings.getValue(OUTPUT_DEVICE_SETTING); + var outputDevices = AudioDevice.getOutputDevices(); + var selectedOutputDevice = AudioDevice.getOutputDevice(); + if (outputDevices.indexOf(outputDeviceSetting) != -1 && selectedOutputDevice != outputDeviceSetting) { + if (AudioDevice.setOutputDevice(outputDeviceSetting)) { + selectedOutputDevice = outputDeviceSetting; + } + } + print("audio output devices: " + outputDevices); + for (var i = 0; i < outputDevices.length; i++) { + var thisDeviceSelected = (outputDevices[i] == selectedOutputDevice); + var menuItem = "Use " + outputDevices[i] + " for Output"; + Menu.addMenuItem({ + menuName: "Audio", + menuItemName: menuItem, + isCheckable: true, + isChecked: thisDeviceSelected + }); + if (thisDeviceSelected) { + selectedOutputMenu = menuItem; + } + } } function onDevicechanged() { diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index dc1d71f402..1dc6b7fef8 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -53,8 +53,11 @@ function updateShowTablet() { if (tabletShown) { + var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone"; + var currentMicEnabled = !Menu.isOptionChecked(MUTE_MICROPHONE_MENU_ITEM); var currentMicLevel = getMicLevel(); var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + tablet.updateMicEnabled(currentMicEnabled); tablet.updateAudioBar(currentMicLevel); }