diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8a2bc3e2b4..8d2439e5b8 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -48,7 +48,7 @@ Dialog { helperText: "domain, location, @user, /x,y,z" anchors.margins: 8 onAccepted: { - event.accepted + event.accepted addressBarDialog.loadAddress(addressLine.text) } } diff --git a/interface/resources/qml/Menu.qml b/interface/resources/qml/InterfaceMenu.qml similarity index 99% rename from interface/resources/qml/Menu.qml rename to interface/resources/qml/InterfaceMenu.qml index 3939794fb2..2a5ed8d212 100644 --- a/interface/resources/qml/Menu.qml +++ b/interface/resources/qml/InterfaceMenu.qml @@ -2,6 +2,7 @@ import QtQuick 2.4 import QtQuick.Controls 1.3 import Hifi 1.0 +// Currently for testing a pure QML replacement menu Item { Item { objectName: "AllActions" @@ -10,7 +11,7 @@ Item { objectName: "HifiAction_" + MenuConstants.AboutApp text: qsTr("About Interface") } - + // // File Menu // @@ -26,7 +27,7 @@ Item { //shortcut: StandardKey.Quit shortcut: "Ctrl+Q" } - + // Scripts Action { id: loadScript @@ -58,7 +59,7 @@ Item { objectName: "HifiAction_" + MenuConstants.StopAllScripts text: qsTr("Stop All Scripts") } - + // Locations Action { id: bookmarkLocation @@ -75,7 +76,7 @@ Item { objectName: "HifiAction_" + MenuConstants.AddressBar text: qsTr("Show Address Bar") } - + // // Edit menu // @@ -84,13 +85,13 @@ Item { text: "Undo" shortcut: StandardKey.Undo } - + Action { id: redo text: "Redo" shortcut: StandardKey.Redo } - + Action { id: animations objectName: "HifiAction_" + MenuConstants.Animations @@ -157,7 +158,7 @@ Item { objectName: "HifiAction_" + MenuConstants.ResetSensors text: qsTr("Reset Sensors") } - + @@ -853,7 +854,7 @@ Item { } } - MenuBar { + Menu { objectName: "rootMenu"; Menu { title: "File" diff --git a/interface/resources/qml/Main.qml b/interface/resources/qml/Main.qml deleted file mode 100644 index 8911a4c9f4..0000000000 --- a/interface/resources/qml/Main.qml +++ /dev/null @@ -1,20 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 1.3 - -ApplicationWindow { - id: root - width: 800 - height: 600 - visible: true - - menuBar: MenuBar { - Menu { - title: "File" - MenuItem { - text: "Quit" - } - } - } - -} - diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index 3a1c92cdf3..f469e33c30 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -9,6 +9,7 @@ Dialog { property real spacing: 8 property real outerSpacing: 16 + destroyOnCloseButton: true destroyOnInvisible: true implicitHeight: content.implicitHeight + outerSpacing * 2 + 48 @@ -35,7 +36,7 @@ Dialog { onEnabledChanged: { if (enabled) { - root.forceActiveFocus(); + root.forceActiveFocus(); } } @@ -334,7 +335,7 @@ Dialog { case Qt.Key_Period: if (Qt.platform.os === "osx") { event.accepted = true - content.reject() + content.reject() } break } else switch (event.key) { @@ -346,7 +347,7 @@ Dialog { case Qt.Key_Enter: case Qt.Key_Return: - console.log("Accepting"); + console.log("Accepting"); event.accepted = true content.accept() break diff --git a/interface/resources/qml/Root.qml b/interface/resources/qml/Root.qml index 9b2e5af6b5..1b0f09558f 100644 --- a/interface/resources/qml/Root.qml +++ b/interface/resources/qml/Root.qml @@ -8,7 +8,7 @@ Root { anchors.fill: parent onParentChanged: { - forceActiveFocus(); + forceActiveFocus(); } } diff --git a/interface/resources/qml/TestRoot.qml b/interface/resources/qml/TestRoot.qml index 0b9209aa76..bd38c696bf 100644 --- a/interface/resources/qml/TestRoot.qml +++ b/interface/resources/qml/TestRoot.qml @@ -6,12 +6,11 @@ import QtQuick.Controls 1.3 import "controls" Root { - id: root + id: root anchors.fill: parent onParentChanged: { - forceActiveFocus(); + forceActiveFocus(); } - Button { id: messageBox anchors.right: createDialog.left @@ -38,7 +37,7 @@ Root { } Keys.onPressed: { - console.log(event.key); + console.log("Key press root") } } diff --git a/interface/resources/qml/HifiMenu.qml b/interface/resources/qml/VrMenu.qml similarity index 87% rename from interface/resources/qml/HifiMenu.qml rename to interface/resources/qml/VrMenu.qml index 2c6963a349..8c4aaafb3b 100644 --- a/interface/resources/qml/HifiMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -5,10 +5,10 @@ import QtQuick.Controls.Styles 1.3 import "controls" import "styles" -Hifi.HifiMenu { +Hifi.VrMenu { id: root anchors.fill: parent - objectName: "HifiMenu" + objectName: "VrMenu" enabled: false opacity: 0.0 property int animationDuration: 200 @@ -17,7 +17,7 @@ Hifi.HifiMenu { onEnabledChanged: { if (enabled && columns.length == 0) { - pushColumn(rootMenu.menus); + pushColumn(rootMenu.items); } opacity = enabled ? 1.0 : 0.0 if (enabled) { @@ -47,13 +47,21 @@ Hifi.HifiMenu { property var menuBuilder: Component { Border { + Component.onCompleted: { + menuDepth = root.models.length - 1 + if (menuDepth == 0) { + x = lastMousePosition.x - 20 + y = lastMousePosition.y - 20 + } else { + var lastColumn = root.columns[menuDepth - 1] + x = lastColumn.x + 64; + y = lastMousePosition.y - height / 2; + } + } SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } - x: root.models.length == 1 ? - (root.width / 2 - width / 2) : - root.columns[root.models.length - 2].x + 60; - anchors.verticalCenter: parent.verticalCenter border.color: hifiPalette.hifiBlue color: sysPalette.window + property int menuDepth ListView { spacing: 6 @@ -186,7 +194,25 @@ Hifi.HifiMenu { anchors.top: parent.top anchors.topMargin: 0 width: listView.width + hoverEnabled: true + Timer { + id: timer + interval: 1000 + onTriggered: parent.select(); + } + onEntered: { + if (source.type == 2 && enabled) { + timer.start() + } + } + onExited: { + timer.stop() + } onClicked: { + select(); + } + function select() { + timer.stop(); listView.currentIndex = listViewIndex parent.root.selectItem(parent.source); } diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index 07162ad1d8..d722d5264a 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -17,7 +17,7 @@ Item { HifiPalette { id: hifiPalette } SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } x: parent ? parent.width / 2 - width / 2 : 0 - y: parent ? parent.height / 2 - height / 2 : 0 + y: parent ? parent.height / 2 - height / 2 : 0 property int animationDuration: 400 property bool destroyOnInvisible: false diff --git a/interface/resources/qml/images/critical.png b/interface/resources/qml/images/critical.png deleted file mode 100644 index dc9c5aebf4..0000000000 Binary files a/interface/resources/qml/images/critical.png and /dev/null differ diff --git a/interface/resources/qml/images/information.png b/interface/resources/qml/images/information.png deleted file mode 100644 index 0a2eb87d10..0000000000 Binary files a/interface/resources/qml/images/information.png and /dev/null differ diff --git a/interface/resources/qml/images/question.png b/interface/resources/qml/images/question.png deleted file mode 100644 index 2dd92fd791..0000000000 Binary files a/interface/resources/qml/images/question.png and /dev/null differ diff --git a/interface/resources/qml/images/warning.png b/interface/resources/qml/images/warning.png deleted file mode 100644 index cba78f6bea..0000000000 Binary files a/interface/resources/qml/images/warning.png and /dev/null differ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b22bacef33..a9bbaf7de0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -52,8 +52,6 @@ #include #include #include -#include -#include #include #include @@ -65,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -135,7 +134,6 @@ #include "ui/DialogsManager.h" #include "ui/InfoView.h" #include "ui/LoginDialog.h" -#include "ui/MarketplaceDialog.h" #include "ui/Snapshot.h" #include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" @@ -510,6 +508,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); + _fullscreenMenuWidget->setParent(_glWidget); + _menuBarHeight = Menu::getInstance()->height(); if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { setFullscreen(true); // Initialize menu bar show/hide } @@ -730,6 +730,10 @@ void Application::initializeGL() { qCDebug(interfaceapp, "Initialized Offscreen UI."); _glWidget->makeCurrent(); + // call Menu getInstance static method to set up the menu + // Needs to happen AFTER the QML UI initialization + _window->setMenuBar(Menu::getInstance()); + init(); qCDebug(interfaceapp, "init() complete."); @@ -764,9 +768,7 @@ void Application::initializeGL() { void Application::initializeUi() { AddressBarDialog::registerType(); LoginDialog::registerType(); - MarketplaceDialog::registerType(); MessageDialog::registerType(); - Menu::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); @@ -902,7 +904,9 @@ void Application::runTests() { } void Application::audioMuteToggled() { - Menu::getInstance()->setIsOptionChecked(MenuOption::MuteAudio, DependencyManager::get()->isMuted()); + QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteAudio); + Q_CHECK_PTR(muteAction); + muteAction->setChecked(DependencyManager::get()->isMuted()); } void Application::aboutApp() { @@ -1067,12 +1071,10 @@ bool Application::eventFilter(QObject* object, QEvent* event) { return false; } -static bool altPressed; -static bool ctrlPressed; +static bool _altPressed{ false }; void Application::keyPressEvent(QKeyEvent* event) { - altPressed = event->key() == Qt::Key_Alt; - ctrlPressed = event->key() == Qt::Key_Control; + _altPressed = event->key() == Qt::Key_Alt; _keysPressed.insert(event->key()); _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts @@ -1162,9 +1164,7 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Backslash: - MarketplaceDialog::show(); - - //Menu::getInstance()->triggerOption(MenuOption::Chat); + Menu::getInstance()->triggerOption(MenuOption::Chat); break; case Qt::Key_Up: @@ -1329,13 +1329,15 @@ void Application::keyPressEvent(QKeyEvent* event) { } void Application::keyReleaseEvent(QKeyEvent* event) { - if (event->key() == Qt::Key_Alt && altPressed && _window->isActiveWindow()) { - Menu::toggle(); + if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) { +#ifndef DEBUG + if (OculusManager::isConnected()) { +#endif + VrMenu::toggle(); +#ifndef DEBUG + } +#endif } - if (event->key() == Qt::Key_Control && ctrlPressed && _window->isActiveWindow()) { - Menu::toggle(); - } - ctrlPressed = altPressed = false; _keysPressed.remove(event->key()); @@ -1346,6 +1348,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { return; } + switch (event->key()) { case Qt::Key_E: case Qt::Key_PageUp: @@ -1442,6 +1445,18 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { return; } + if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen) + && !Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { + // Show/hide menu bar in fullscreen + if (event->globalY() > _menuBarHeight) { + _fullscreenMenuWidget->setFixedHeight(0); + Menu::getInstance()->setFixedHeight(0); + } else { + _fullscreenMenuWidget->setFixedHeight(_menuBarHeight); + Menu::getInstance()->setFixedHeight(_menuBarHeight); + } + } + _entities.mouseMoveEvent(event, deviceID); _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts @@ -1449,6 +1464,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { if (_controllerScriptingInterface.isMouseCaptured()) { return; } + } void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { @@ -1735,6 +1751,34 @@ void Application::setFullscreen(bool fullscreen) { Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(fullscreen); } +// The following code block is useful on platforms that can have a visible +// app menu in a fullscreen window. However the OSX mechanism hides the +// application menu for fullscreen apps, so the check is not required. +#ifndef Q_OS_MAC + if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { + if (fullscreen) { + // Menu hide() disables menu commands, and show() after hide() doesn't work with Rift VR display. + // So set height instead. + _window->menuBar()->setMaximumHeight(0); + } else { + _window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX); + } + } else { + if (fullscreen) { + // Move menu to a QWidget floating above _glWidget so that show/hide doesn't adjust viewport. + _menuBarHeight = Menu::getInstance()->height(); + Menu::getInstance()->setParent(_fullscreenMenuWidget); + Menu::getInstance()->setFixedWidth(_window->windowHandle()->screen()->size().width()); + _fullscreenMenuWidget->show(); + } else { + // Restore menu to being part of MainWindow. + _fullscreenMenuWidget->hide(); + _window->setMenuBar(Menu::getInstance()); + _window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX); + } + } +#endif + // Work around Qt bug that prevents floating menus being shown when in fullscreen mode. // https://bugreports.qt.io/browse/QTBUG-41883 // Known issue: Top-level menu items don't highlight when cursor hovers. This is probably a side-effect of the work-around. @@ -1987,18 +2031,16 @@ void Application::init() { OculusManager::connect(); if (OculusManager::isConnected()) { - // perform as a post-event so that the code is run after init is complete - qApp->postLambdaEvent([] { - Menu::getInstance()->triggerOption(MenuOption::Fullscreen); - }); + QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), + "trigger", + Qt::QueuedConnection); } TV3DManager::connect(); if (TV3DManager::isConnected()) { - // perform as a post-event so that the code is run after init is complete - qApp->postLambdaEvent([] { - Menu::getInstance()->triggerOption(MenuOption::Fullscreen); - }); + QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), + "trigger", + Qt::QueuedConnection); } _timerStart.start(); diff --git a/interface/src/Application.h b/interface/src/Application.h index f63f548c47..688cf23876 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -623,6 +623,9 @@ private: void checkSkeleton(); + QWidget* _fullscreenMenuWidget = new QWidget(); + int _menuBarHeight; + QHash _acceptedExtensions; QList _domainConnectionRefusals; diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 76ab607ff5..896a50acca 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -84,16 +84,13 @@ void Bookmarks::persistToFile() { saveFile.write(data); } -void Bookmarks::setupMenus(const QString & parentMenu) { +void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { // Add menus/actions - Menu * menu = Menu::getInstance(); - menu->addItem(parentMenu, MenuOption::BookmarkLocation, [this] { - bookmarkLocation(); - }); - menu->addMenu(parentMenu, MenuOption::Bookmarks); - menu->addItem(parentMenu, MenuOption::DeleteBookmark, [this] { - deleteBookmark(); - }); + menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation, 0, + this, SLOT(bookmarkLocation())); + _bookmarksMenu = menu->addMenu(MenuOption::Bookmarks); + _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark, 0, + this, SLOT(deleteBookmark())); // Enable/Disable menus as needed enableMenuItems(_bookmarks.count() > 0); @@ -102,7 +99,7 @@ void Bookmarks::setupMenus(const QString & parentMenu) { for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it ) { QString bookmarkName = it.key(); QString bookmarkAddress = it.value().toString(); - addLocationToMenu(bookmarkName, bookmarkAddress); + addLocationToMenu(menubar, bookmarkName, bookmarkAddress); } } @@ -137,20 +134,29 @@ void Bookmarks::bookmarkLocation() { if (duplicateBookmarkMessage.exec() == QMessageBox::No) { return; } - removeLocationFromMenu(bookmarkName); + removeLocationFromMenu(menubar, bookmarkName); } - addLocationToMenu(bookmarkName, bookmarkAddress); + addLocationToMenu(menubar, bookmarkName, bookmarkAddress); insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName. enableMenuItems(true); } +void Bookmarks::teleportToBookmark() { + QAction* action = qobject_cast(sender()); + QString address = action->data().toString(); + DependencyManager::get()->handleLookupString(address); +} + void Bookmarks::deleteBookmark() { + QStringList bookmarkList; - bookmarkList.append(_bookmarks.keys()); - - + QList menuItems = _bookmarksMenu->actions(); + for (int i = 0; i < menuItems.count(); i += 1) { + bookmarkList.append(menuItems[i]->text()); + } + QInputDialog deleteBookmarkDialog(qApp->getWindow()); deleteBookmarkDialog.setWindowTitle("Delete Bookmark"); deleteBookmarkDialog.setLabelText("Select the bookmark to delete"); @@ -168,29 +174,34 @@ void Bookmarks::deleteBookmark() { return; } - removeLocationFromMenu(bookmarkName); + removeLocationFromMenu(Menu::getInstance(), bookmarkName); remove(bookmarkName); - - if (_bookmarks.keys().isEmpty()) { + + if (_bookmarksMenu->actions().count() == 0) { enableMenuItems(false); } } void Bookmarks::enableMenuItems(bool enabled) { - Menu* menu = Menu::getInstance(); - menu->enableItem(MenuOption::Bookmarks, enabled); - menu->enableItem(MenuOption::DeleteBookmark, enabled); + if (_bookmarksMenu) { + _bookmarksMenu->setEnabled(enabled); + } + if (_deleteBookmarksAction) { + _deleteBookmarksAction->setEnabled(enabled); + } } -void Bookmarks::addLocationToMenu(QString& name, QString& address) { - - Menu::getInstance()->addItem(MenuOption::Bookmarks, name, [=] { - DependencyManager::get()->handleLookupString(address); - }); +void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) { + QAction* teleportAction = _bookmarksMenu->newAction(); + teleportAction->setData(address); + connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark())); + + menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, + name, 0, QAction::NoRole); } -void Bookmarks::removeLocationFromMenu(QString& name) { - Menu::getInstance()->removeItem(name); +void Bookmarks::removeLocationFromMenu(Menu* menubar, QString& name) { + menubar->removeAction(_bookmarksMenu, name); } diff --git a/interface/src/Bookmarks.h b/interface/src/Bookmarks.h index d996d7c0f0..7ff9d48e8a 100644 --- a/interface/src/Bookmarks.h +++ b/interface/src/Bookmarks.h @@ -19,6 +19,7 @@ class QAction; class QMenu; class Menu; +class MenuWrapper; class Bookmarks: public QObject { Q_OBJECT @@ -26,16 +27,19 @@ class Bookmarks: public QObject { public: Bookmarks(); - void setupMenus(const QString & menu); + void setupMenus(Menu* menubar, MenuWrapper* menu); private slots: void bookmarkLocation(); + void teleportToBookmark(); void deleteBookmark(); private: - // FIXME bookmarks should be more categorizable - // Can we leverage a system browser favorites API? QVariantMap _bookmarks; // { name: address, ... } + + QPointer _bookmarksMenu; + QPointer _deleteBookmarksAction; + const QString BOOKMARKS_FILENAME = "bookmarks.json"; QString _bookmarksFilename; @@ -47,8 +51,8 @@ private: void persistToFile(); void enableMenuItems(bool enabled); - void addLocationToMenu(QString& name, QString& address); - void removeLocationFromMenu(QString& name); + void addLocationToMenu(Menu* menubar, QString& name, QString& address); + void removeLocationFromMenu(Menu* menubar, QString& name); }; #endif // hifi_Bookmarks_h \ No newline at end of file diff --git a/interface/src/MainWindow.cpp b/interface/src/MainWindow.cpp index 7e8d46f5ee..f60716dc48 100644 --- a/interface/src/MainWindow.cpp +++ b/interface/src/MainWindow.cpp @@ -94,9 +94,9 @@ void MainWindow::changeEvent(QEvent* event) { emit windowShown(true); } - //if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { - // Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen()); - //} + if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { + Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen()); + } } else if (event->type() == QEvent::ActivationChange) { if (isActiveWindow()) { emit windowShown(true); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8c45f583f1..67fae46b33 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -21,7 +20,8 @@ #include #include #include -#include +#include + #include "Application.h" #include "AccountManager.h" #include "audio/AudioIOStatsRenderer.h" @@ -42,625 +42,529 @@ #include "InterfaceLogging.h" #include "Menu.h" -#include "Util.h" -// Proxy object to simplify porting over -HifiAction::HifiAction(const QString& menuOption) : _menuOption(menuOption) { -} - -void HifiAction::setCheckable(bool checkable) { - Menu::getInstance()->setCheckable(_menuOption, checkable); -} - -void HifiAction::setChecked(bool checked) { - Menu::getInstance()->setChecked(_menuOption, checked); -} - -void HifiAction::setVisible(bool visible) { - return Menu::getInstance()->setItemVisible(_menuOption); -} - -QString HifiAction::shortcut() const { - return Menu::getInstance()->getItemShortcut(_menuOption); -} - -void HifiAction::setText(const QString& text) { - Menu::getInstance()->setItemText(_menuOption, text); -} - -void HifiAction::setTriggerAction(std::function f) { - Menu::getInstance()->setTriggerAction(_menuOption, f); -} - -void HifiAction::setToggleAction(std::function f) { - Menu::getInstance()->setToggleAction(_menuOption, f); -} - -Menu* Menu::_instance = nullptr; +Menu* Menu::_instance = NULL; Menu* Menu::getInstance() { - // Optimistic check for menu existence + static QMutex menuInstanceMutex; + + // lock the menu instance mutex to make sure we don't race and create two menus and crash + menuInstanceMutex.lock(); + if (!_instance) { - static QMutex menuInstanceMutex; - withLock(menuInstanceMutex, [] { - if (!_instance) { - qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); - qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); - Menu::load(); - auto uiRoot = DependencyManager::get()->getRootItem(); - _instance = uiRoot->findChild(NAME); - if (!_instance) { - qFatal("Could not load menu QML"); - } else { - _instance->init(); - } - } - }); + qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); + _instance = new Menu(); } + + menuInstanceMutex.unlock(); + return _instance; } -Menu::Menu(QQuickItem* parent) : HifiMenu(parent) { -} - -void Menu::init() { +Menu::Menu() { + MenuWrapper * fileMenu = addMenu("File"); +#ifdef Q_OS_MAC + addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); +#endif auto dialogsManager = DependencyManager::get(); AccountManager& accountManager = AccountManager::getInstance(); - static const QString ROOT_MENU; + { - static const QString FILE_MENU{ "File" }; - addMenu(ROOT_MENU, FILE_MENU); - { - addItem(FILE_MENU, MenuOption::Login); - // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item - connect(&accountManager, &AccountManager::profileChanged, + addActionToQMenuAndActionHash(fileMenu, MenuOption::Login); + + // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item + connect(&accountManager, &AccountManager::profileChanged, dialogsManager.data(), &DialogsManager::toggleLoginDialog); - connect(&accountManager, &AccountManager::logoutComplete, + connect(&accountManager, &AccountManager::logoutComplete, dialogsManager.data(), &DialogsManager::toggleLoginDialog); - } - -#ifdef Q_OS_MAC - addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); -#endif - - { -#if 0 - static const QString SCRIPTS_MENU{ "Scripts" }; - addMenu(FILE_MENU, LOCATION_MENU); -#else - static const QString SCRIPTS_MENU{ FILE_MENU }; - addSeparator(FILE_MENU, "Scripts"); -#endif - //Qt::CTRL | Qt::Key_O - addItem(SCRIPTS_MENU, MenuOption::LoadScript, qApp, SLOT(loadDialog())); - //Qt::CTRL | Qt::SHIFT | Qt::Key_O - addItem(SCRIPTS_MENU, MenuOption::LoadScriptURL, [=] { - qApp->loadScriptURLDialog(); - }); - addItem(SCRIPTS_MENU, MenuOption::StopAllScripts, [=] { - qApp->stopAllScripts(); - }); - //Qt::CTRL | Qt::Key_R, - addItem(SCRIPTS_MENU, MenuOption::ReloadAllScripts, [=] { - qApp->reloadAllScripts(); - }); - // Qt::CTRL | Qt::Key_J, - addItem(SCRIPTS_MENU, MenuOption::RunningScripts, [=] { - qApp->toggleRunningScriptsWidget(); - }); - } - - { -#if 0 - static const QString LOCATION_MENU{ "Location" }; - addMenu(FILE_MENU, LOCATION_MENU); -#else - addSeparator(FILE_MENU, "Location"); - static const QString LOCATION_MENU{ FILE_MENU }; -#endif - qApp->getBookmarks()->setupMenus(LOCATION_MENU); - - //Qt::CTRL | Qt::Key_L - addItem(LOCATION_MENU, MenuOption::AddressBar, [=] { - auto dialogsManager = DependencyManager::get(); - dialogsManager->toggleAddressBar(); - }); - addItem(LOCATION_MENU, MenuOption::CopyAddress, [=] { - auto addressManager = DependencyManager::get(); - addressManager->copyAddress(); - }); - addItem(LOCATION_MENU, MenuOption::CopyPath, [=] { - auto addressManager = DependencyManager::get(); - addressManager->copyPath(); - }); - } - - // Qt::CTRL | Qt::Key_Q - // QAction::QuitRole - addItem(FILE_MENU, MenuOption::Quit, [=] { - qApp->quit(); - }); } + + addDisabledActionAndSeparator(fileMenu, "Scripts"); + addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, + qApp, SLOT(loadDialog())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL, + Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R, + qApp, SLOT(reloadAllScripts())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, + qApp, SLOT(toggleRunningScriptsWidget())); + + addDisabledActionAndSeparator(fileMenu, "Location"); + qApp->getBookmarks()->setupMenus(this, fileMenu); + + addActionToQMenuAndActionHash(fileMenu, + MenuOption::AddressBar, + Qt::CTRL | Qt::Key_L, + dialogsManager.data(), + SLOT(toggleAddressBar())); + auto addressManager = DependencyManager::get(); + addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0, + addressManager.data(), SLOT(copyAddress())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, + addressManager.data(), SLOT(copyPath())); + + addActionToQMenuAndActionHash(fileMenu, + MenuOption::Quit, + Qt::CTRL | Qt::Key_Q, + qApp, + SLOT(quit()), + QAction::QuitRole); - { - static const QString EDIT_MENU{ "Edit" }; - addMenu(ROOT_MENU, EDIT_MENU); + MenuWrapper* editMenu = addMenu("Edit"); - QUndoStack* undoStack = qApp->getUndoStack(); - QAction* undoAction = undoStack->createUndoAction(this); - //undoAction->setShortcut(Qt::CTRL | Qt::Key_Z); - addItem(EDIT_MENU, undoAction->text(), undoAction, SLOT(trigger())); + QUndoStack* undoStack = qApp->getUndoStack(); + QAction* undoAction = undoStack->createUndoAction(editMenu); + undoAction->setShortcut(Qt::CTRL | Qt::Key_Z); + addActionToQMenuAndActionHash(editMenu, undoAction); - QAction* redoAction = undoStack->createRedoAction(this); - //redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z); - addItem(EDIT_MENU, redoAction->text(), redoAction, SLOT(trigger())); + QAction* redoAction = undoStack->createRedoAction(editMenu); + redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z); + addActionToQMenuAndActionHash(editMenu, redoAction); - // Qt::CTRL | Qt::Key_Comma - // QAction::PreferencesRole - addItem(EDIT_MENU, MenuOption::Preferences, [=] { - dialogsManager->editPreferences(); - }); - addItem(EDIT_MENU, MenuOption::Animations, [=] { - dialogsManager->editAnimations(); - }); - } + addActionToQMenuAndActionHash(editMenu, + MenuOption::Preferences, + Qt::CTRL | Qt::Key_Comma, + dialogsManager.data(), + SLOT(editPreferences()), + QAction::PreferencesRole); - { - static const QString TOOLS_MENU{ "Tools" }; - addMenu(ROOT_MENU, TOOLS_MENU); + addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, + dialogsManager.data(), SLOT(editAttachments())); + addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0, + dialogsManager.data(), SLOT(editAnimations())); + + MenuWrapper* toolsMenu = addMenu("Tools"); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, + dialogsManager.data(), SLOT(showScriptEditor())); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) - auto speechRecognizer = DependencyManager::get(); - //Qt::CTRL | Qt::SHIFT | Qt::Key_C - addItem(TOOLS_MENU, MenuOption::ControlWithSpeech); - setChecked(MenuOption::ControlWithSpeech, speechRecognizer->getEnabled()); - connect(speechRecognizer.data(), &SpeechRecognizer::enabledUpdated, [=] { - setChecked(MenuOption::ControlWithSpeech, speechRecognizer->getEnabled()); - }); + auto speechRecognizer = DependencyManager::get(); + QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::ControlWithSpeech, + Qt::CTRL | Qt::SHIFT | Qt::Key_C, + speechRecognizer->getEnabled(), + speechRecognizer.data(), + SLOT(setEnabled(bool))); + connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool))); #endif - // Qt::ALT | Qt::Key_S, - addItem(TOOLS_MENU, MenuOption::ScriptEditor, [=] { - dialogsManager->showScriptEditor(); - }); - // QML Qt::Key_Backslash, - addItem(TOOLS_MENU, MenuOption::Chat, [=] { - dialogsManager->showIRCLink(); - }); - addItem(TOOLS_MENU, MenuOption::AddRemoveFriends, [=] { - qApp->showFriendsWindow(); - }); + + addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, + 0, // QML Qt::Key_Backslash, + dialogsManager.data(), SLOT(showIRCLink())); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0, + qApp, SLOT(showFriendsWindow())); - { - static const QString VIZ_MENU{ "I Am Visible To" }; - addMenu(TOOLS_MENU, VIZ_MENU); - auto discoverabilityManager = DependencyManager::get(); - // FIXME group - addCheckableItem(VIZ_MENU, MenuOption::VisibleToEveryone, - discoverabilityManager->getDiscoverabilityMode() == Discoverability::All, - discoverabilityManager.data(), SLOT(setVisibility())); - addCheckableItem(VIZ_MENU, MenuOption::VisibleToFriends, - discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends, - discoverabilityManager.data(), SLOT(setVisibility())); - addCheckableItem(VIZ_MENU, MenuOption::VisibleToNoOne, - discoverabilityManager->getDiscoverabilityMode() == Discoverability::None, - discoverabilityManager.data(), SLOT(setVisibility())); - connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, - discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged); - } + MenuWrapper* visibilityMenu = toolsMenu->addMenu("I Am Visible To"); + { + QActionGroup* visibilityGroup = new QActionGroup(toolsMenu); + auto discoverabilityManager = DependencyManager::get(); - //Qt::CTRL | Qt::ALT | Qt::Key_T, - addItem(TOOLS_MENU, MenuOption::ToolWindow, - dialogsManager.data(), SLOT(toggleToolWindow())); + QAction* visibleToEveryone = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToEveryone, + 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::All, + discoverabilityManager.data(), SLOT(setVisibility())); + visibilityGroup->addAction(visibleToEveryone); - //Qt::CTRL | Qt::ALT | Qt::Key_J, - addItem(TOOLS_MENU, MenuOption::Console, [=] { - DependencyManager::get()->toggleConsole(); - }); + QAction* visibleToFriends = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToFriends, + 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends, + discoverabilityManager.data(), SLOT(setVisibility())); + visibilityGroup->addAction(visibleToFriends); - // QML Qt::Key_Apostrophe, - addItem(TOOLS_MENU, MenuOption::ResetSensors, [=] { - qApp->resetSensors(); - }); + QAction* visibleToNoOne = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToNoOne, + 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None, + discoverabilityManager.data(), SLOT(setVisibility())); + visibilityGroup->addAction(visibleToNoOne); - addItem(TOOLS_MENU, MenuOption::PackageModel, [=] { - qApp->packageModel(); - }); + connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, + discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged); } - { - static const QString AVATAR_MENU{ "Avatar" }; - addMenu(ROOT_MENU, AVATAR_MENU); - auto avatar = DependencyManager::get()->getMyAvatar(); - { - static const QString SIZE_MENU{ "Size" }; - addMenu(AVATAR_MENU, SIZE_MENU); - // QML Qt::Key_Plus, - addItem(SIZE_MENU, MenuOption::IncreaseAvatarSize, [=] { - avatar->increaseSize(); - }); - // QML Qt::Key_Minus, - addItem(SIZE_MENU, MenuOption::DecreaseAvatarSize, [=] { - avatar->decreaseSize(); - }); - // QML Qt::Key_Equal, - addItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { - avatar->resetSize(); - }); + addActionToQMenuAndActionHash(toolsMenu, + MenuOption::ToolWindow, + Qt::CTRL | Qt::ALT | Qt::Key_T, + dialogsManager.data(), + SLOT(toggleToolWindow())); - addItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { - avatar->resetSize(); - }); - } + addActionToQMenuAndActionHash(toolsMenu, + MenuOption::Console, + Qt::CTRL | Qt::ALT | Qt::Key_J, + DependencyManager::get().data(), + SLOT(toggleConsole())); - //Qt::CTRL | Qt::SHIFT | Qt::Key_K - addCheckableItem(AVATAR_MENU, MenuOption::KeyboardMotorControl, true, [=](bool) { - avatar->updateMotionBehavior(); - }); - addCheckableItem(AVATAR_MENU, MenuOption::ScriptedMotorControl, true); - addCheckableItem(AVATAR_MENU, MenuOption::NamesAboveHeads, true); - addCheckableItem(AVATAR_MENU, MenuOption::GlowWhenSpeaking, true); - addCheckableItem(AVATAR_MENU, MenuOption::BlueSpeechSphere, true); - addCheckableItem(AVATAR_MENU, MenuOption::EnableCharacterController, true, + addActionToQMenuAndActionHash(toolsMenu, + MenuOption::ResetSensors, + 0, // QML Qt::Key_Apostrophe, + qApp, + SLOT(resetSensors())); + + addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, + qApp, SLOT(packageModel())); + + MenuWrapper* avatarMenu = addMenu("Avatar"); + QObject* avatar = DependencyManager::get()->getMyAvatar(); + + MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size"); + addActionToQMenuAndActionHash(avatarSizeMenu, + MenuOption::IncreaseAvatarSize, + 0, // QML Qt::Key_Plus, + avatar, + SLOT(increaseSize())); + addActionToQMenuAndActionHash(avatarSizeMenu, + MenuOption::DecreaseAvatarSize, + 0, // QML Qt::Key_Minus, + avatar, + SLOT(decreaseSize())); + addActionToQMenuAndActionHash(avatarSizeMenu, + MenuOption::ResetAvatarSize, + 0, // QML Qt::Key_Equal, + avatar, + SLOT(resetSize())); + + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::KeyboardMotorControl, + Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehavior())); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ScriptedMotorControl, 0, true, avatar, SLOT(updateMotionBehavior())); - addCheckableItem(AVATAR_MENU, MenuOption::ShiftHipsForIdleAnimations, false, + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true, + avatar, SLOT(updateMotionBehavior())); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false, avatar, SLOT(updateMotionBehavior())); - } - { - static const QString VIEW_MENU{ "View" }; - addMenu(ROOT_MENU, VIEW_MENU); + MenuWrapper* viewMenu = addMenu("View"); - // Mac Qt::CTRL | Qt::META | Qt::Key_F, - // Win32/Linux Qt::CTRL | Qt::Key_F, - addCheckableItem(VIEW_MENU, MenuOption::Fullscreen, false); - connectCheckable(MenuOption::Fullscreen, qApp, SLOT(setFullscreen(bool))); - // QML Qt::Key_P, - addCheckableItem(VIEW_MENU, MenuOption::FirstPerson, true, - qApp, SLOT(cameraMenuChanged())); - //QML Qt::SHIFT | Qt::Key_H, - addCheckableItem(VIEW_MENU, MenuOption::Mirror, true, - qApp, SLOT(cameraMenuChanged())); - // QML Qt::Key_H, - addCheckableItem(VIEW_MENU, MenuOption::FullscreenMirror, false, - qApp, SLOT(cameraMenuChanged())); - - - // Mac Qt::META | Qt::Key_H, - // Win32/Linux Qt::CTRL | Qt::Key_H, - addCheckableItem(VIEW_MENU, MenuOption::HMDTools, false, [=](bool checked) { - dialogsManager->hmdTools(checked); - }); - addCheckableItem(VIEW_MENU, MenuOption::EnableVRMode, false); - connectCheckable(MenuOption::EnableVRMode, qApp, SLOT(setEnableVRMode(bool))); - addCheckableItem(VIEW_MENU, MenuOption::Enable3DTVMode, false); - connectCheckable(MenuOption::Enable3DTVMode, qApp, SLOT(setEnable3DTVMode(bool))); - - { - static const QString BORDER_MENU{ "Server Borders" }; - addMenu(VIEW_MENU, BORDER_MENU); - // Qt::CTRL | Qt::SHIFT | Qt::Key_1 - addCheckableItem(BORDER_MENU, MenuOption::ShowBordersEntityNodes, false, [=](bool checked) { - qApp->getNodeBoundsDisplay().setShowEntityNodes(checked); - }); - } - addCheckableItem(VIEW_MENU, MenuOption::OffAxisProjection, false); - addCheckableItem(VIEW_MENU, MenuOption::TurnWithHead, false); - // QML Qt::Key_Slash - addCheckableItem(VIEW_MENU, MenuOption::Stats, false); - - // Qt::CTRL | Qt::SHIFT | Qt::Key_L - addItem(VIEW_MENU, MenuOption::Log, [=] { - qApp->toggleLogDialog(); - }); - addItem(VIEW_MENU, MenuOption::BandwidthDetails, [=] { - dialogsManager->bandwidthDetails(); - }); - addItem(VIEW_MENU, MenuOption::OctreeStats, [=] { - dialogsManager->octreeStatsDetails(); - }); - } - - { - static const QString DEV_MENU{ "Developer" }; - addMenu(ROOT_MENU, DEV_MENU); - { - static const QString RENDER_MENU{ "Render" }; - addMenu(DEV_MENU, RENDER_MENU); - // QML Qt::SHIFT | Qt::Key_A, - addCheckableItem(RENDER_MENU, MenuOption::Atmosphere, true); - addCheckableItem(RENDER_MENU, MenuOption::AmbientOcclusion); - addCheckableItem(RENDER_MENU, MenuOption::DontFadeOnOctreeServerChanges); - { - static const QString LIGHT_MENU{ MenuOption::RenderAmbientLight }; - addMenu(RENDER_MENU, LIGHT_MENU); - static QStringList LIGHTS{ - MenuOption::RenderAmbientLightGlobal, - MenuOption::RenderAmbientLight0, - MenuOption::RenderAmbientLight1, - MenuOption::RenderAmbientLight2, - MenuOption::RenderAmbientLight3, - MenuOption::RenderAmbientLight4, - MenuOption::RenderAmbientLight5, - MenuOption::RenderAmbientLight6, - MenuOption::RenderAmbientLight7, - MenuOption::RenderAmbientLight8, - MenuOption::RenderAmbientLight9, - }; - foreach(QString option, LIGHTS) { - addCheckableItem(LIGHT_MENU, option); - // FIXME - // setExclusiveGroup() - } - setChecked(MenuOption::RenderAmbientLightGlobal, true); - } - { - static const QString SHADOWS_MENU{ "Shadows" }; - addMenu(RENDER_MENU, SHADOWS_MENU); - addCheckableItem(SHADOWS_MENU, "No Shadows", true); - addCheckableItem(SHADOWS_MENU, MenuOption::SimpleShadows); - addCheckableItem(SHADOWS_MENU, MenuOption::CascadedShadows); - } - { - static const QString FRAMERATE_MENU{ MenuOption::RenderTargetFramerate }; - addMenu(RENDER_MENU, FRAMERATE_MENU); - //framerateGroup->setExclusive(true); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerateUnlimited, true); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate60); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate50); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate40); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate30); - } -#if !defined(Q_OS_MAC) - addCheckableItem(RENDER_MENU, MenuOption::RenderTargetFramerateVSyncOn, true, [](bool checked) { - qApp->setVSyncEnabled(); - }); + addCheckableActionToQMenuAndActionHash(viewMenu, + MenuOption::Fullscreen, +#ifdef Q_OS_MAC + Qt::CTRL | Qt::META | Qt::Key_F, +#else + Qt::CTRL | Qt::Key_F, #endif - { - static const QString RES_MENU{ MenuOption::RenderResolution }; - addMenu(RENDER_MENU, RES_MENU); - // resolutionGroup->setExclusive(true); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionOne, true); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionTwoThird); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionHalf); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionThird); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionQuarter); - } - // QML Qt::Key_Asterisk, - addCheckableItem(RENDER_MENU, MenuOption::Stars, true); - addCheckableItem(RENDER_MENU, MenuOption::EnableGlowEffect, true, [](bool checked){ - DependencyManager::get()->toggleGlowEffect(checked); - }); - //Qt::ALT | Qt::Key_W - addCheckableItem(RENDER_MENU, MenuOption::Wireframe); - // QML Qt::SHIFT | Qt::Key_L, - addItem(RENDER_MENU, MenuOption::LodTools, [=] { - dialogsManager->lodTools(); - }); - } // Developer -> Render + false, + qApp, + SLOT(setFullscreen(bool))); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, + 0, // QML Qt::Key_P, + true, qApp, SLOT(cameraMenuChanged())); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, + 0, //QML Qt::SHIFT | Qt::Key_H, + true); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, + 0, // QML Qt::Key_H, + false, qApp, SLOT(cameraMenuChanged())); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, +#ifdef Q_OS_MAC + Qt::META | Qt::Key_H, +#else + Qt::CTRL | Qt::Key_H, +#endif + false, + dialogsManager.data(), + SLOT(hmdTools(bool))); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0, + false, + qApp, + SLOT(setEnableVRMode(bool))); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, + false, + qApp, + SLOT(setEnable3DTVMode(bool))); + + + MenuWrapper* nodeBordersMenu = viewMenu->addMenu("Server Borders"); + NodeBounds& nodeBounds = qApp->getNodeBoundsDisplay(); + addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersEntityNodes, + Qt::CTRL | Qt::SHIFT | Qt::Key_1, false, + &nodeBounds, SLOT(setShowEntityNodes(bool))); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false); + + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, + 0); // QML Qt::Key_Slash); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats); + addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, + Qt::CTRL | Qt::SHIFT | Qt::Key_L, + qApp, SLOT(toggleLogDialog())); + addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, + dialogsManager.data(), SLOT(bandwidthDetails())); + addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, + dialogsManager.data(), SLOT(octreeStatsDetails())); + + + MenuWrapper* developerMenu = addMenu("Developer"); + + MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, + 0, // QML Qt::SHIFT | Qt::Key_A, + true); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges); + + MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight); + QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu); + ambientLightGroup->setExclusive(true); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLightGlobal, 0, true)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight0, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight1, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight2, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight3, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight4, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight5, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight6, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight7, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); + + MenuWrapper* shadowMenu = renderOptionsMenu->addMenu("Shadows"); + QActionGroup* shadowGroup = new QActionGroup(shadowMenu); + shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true)); + shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false)); + shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false)); + + { + MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate); + QActionGroup* framerateGroup = new QActionGroup(framerateMenu); + framerateGroup->setExclusive(true); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true)); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false)); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false)); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false)); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false)); + +#if defined(Q_OS_MAC) +#else + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, + qApp, SLOT(setVSyncEnabled())); +#endif + } + + + MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); + QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); + resolutionGroup->setExclusive(true); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false)); + + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, + 0, // QML Qt::Key_Asterisk, + true); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true, + DependencyManager::get().data(), SLOT(toggleGlowEffect(bool))); + + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false); + addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, + 0, // QML Qt::SHIFT | Qt::Key_L, + dialogsManager.data(), SLOT(lodTools())); + + MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); + + MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); + { + QActionGroup* faceTrackerGroup = new QActionGroup(avatarDebugMenu); + + QAction* noFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::NoFaceTracking, + 0, true, + qApp, SLOT(setActiveFaceTracker())); + faceTrackerGroup->addAction(noFaceTracker); - { - static const QString AVATAR_MENU{ "Avatar Dev" }; - addMenu(DEV_MENU, AVATAR_MENU); - setItemText(AVATAR_MENU, "Avatar"); - { - static const QString FACE_MENU{ "Face Tracking" }; - addMenu(AVATAR_MENU, FACE_MENU); - // FIXME GROUP - addCheckableItem(FACE_MENU, MenuOption::NoFaceTracking, true, [](bool checked) { - qApp->setActiveFaceTracker(); - }); #ifdef HAVE_FACESHIFT - addCheckableItem(FACE_MENU, MenuOption::Faceshift, true, [](bool checked) { - qApp->setActiveFaceTracker(); - }); + QAction* faceshiftFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::Faceshift, + 0, false, + qApp, SLOT(setActiveFaceTracker())); + faceTrackerGroup->addAction(faceshiftFaceTracker); #endif #ifdef HAVE_DDE - addCheckableItem(FACE_MENU, MenuOption::UseCamera, true, [](bool checked) { - qApp->setActiveFaceTracker(); - }); + QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera, + 0, false, + qApp, SLOT(setActiveFaceTracker())); + faceTrackerGroup->addAction(ddeFaceTracker); +#endif + } +#ifdef HAVE_DDE + faceTrackingMenu->addSeparator(); + QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true); + useAudioForMouth->setVisible(false); + QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true); + ddeFiltering->setVisible(false); #endif - addCheckableItem(FACE_MENU, MenuOption::UseAudioForMouth, true); - // FIXME integrate the visibility into the main API - getActionForOption(MenuOption::UseAudioForMouth)->setVisible(false); - addCheckableItem(FACE_MENU, MenuOption::VelocityFilter, true); - // FIXME integrate the visibility into the main API - getActionForOption(MenuOption::VelocityFilter)->setVisible(false); - } - addCheckableItem(AVATAR_MENU, MenuOption::RenderSkeletonCollisionShapes); - addCheckableItem(AVATAR_MENU, MenuOption::RenderHeadCollisionShapes); - addCheckableItem(AVATAR_MENU, MenuOption::RenderBoundingCollisionShapes); - addCheckableItem(AVATAR_MENU, MenuOption::RenderLookAtVectors); - addCheckableItem(AVATAR_MENU, MenuOption::RenderFocusIndicator); - { - static const QString HANDS_MENU{ "Hands" }; - addMenu(AVATAR_MENU, HANDS_MENU); - addCheckableItem(HANDS_MENU, MenuOption::AlignForearmsWithWrists); - addCheckableItem(HANDS_MENU, MenuOption::AlternateIK); - addCheckableItem(HANDS_MENU, MenuOption::DisplayHands, true); - addCheckableItem(HANDS_MENU, MenuOption::DisplayHandTargets); - addCheckableItem(HANDS_MENU, MenuOption::ShowIKConstraints); - { - static const QString SIXENSE_MENU{ "Sixense" }; - addMenu(HANDS_MENU, SIXENSE_MENU); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); + + MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false); + + MenuWrapper* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense"); #ifdef __APPLE__ - addCheckableItem(SIXENSE_MENU, MenuOption::SixenseEnabled, false, [](bool checked) { - SixenseManager::getInstance().toggleSixense(checked); - }); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, + MenuOption::SixenseEnabled, + 0, false, + &SixenseManager::getInstance(), + SLOT(toggleSixense(bool))); #endif - addCheckableItem(SIXENSE_MENU, MenuOption::FilterSixense, true, [](bool checked) { - SixenseManager::getInstance().setFilter(checked); - }); - addCheckableItem(SIXENSE_MENU, MenuOption::LowVelocityFilter, true, [](bool checked) { - qApp->setLowVelocityFilter(checked); - }); - addCheckableItem(SIXENSE_MENU, MenuOption::SixenseMouseInput, true); - addCheckableItem(SIXENSE_MENU, MenuOption::SixenseLasers); - } + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, + MenuOption::FilterSixense, + 0, + true, + &SixenseManager::getInstance(), + SLOT(setFilter(bool))); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, + MenuOption::LowVelocityFilter, + 0, + true, + qApp, + SLOT(setLowVelocityFilter(bool))); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false); - { - static const QString LEAP_MENU{ "Leap Motion" }; - addMenu(HANDS_MENU, LEAP_MENU); - addCheckableItem(LEAP_MENU, MenuOption::LeapMotionOnHMD); - addCheckableItem(LEAP_MENU, MenuOption::LeapMotionOnHMD); - } + MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); + addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false); #ifdef HAVE_RSSDK - { - static const QString RSS_MENU{ "RealSense" }; - addMenu(HANDS_MENU, RSS_MENU); - addItem(RSS_MENU, MenuOption::LoadRSSDKFile, [] { - RealSense::getInstance()->loadRSSDKFile(); - }); - addCheckableItem(RSS_MENU, MenuOption::LeapMotionOnHMD); - } + QMenu* realSenseOptionsMenu = handOptionsMenu->addMenu("RealSense"); + addActionToQMenuAndActionHash(realSenseOptionsMenu, MenuOption::LoadRSSDKFile, 0, + RealSense::getInstance(), SLOT(loadRSSDKFile())); #endif - } // Developer -> Hands - { - static const QString NETWORK_MENU{ "Network" }; - addMenu(DEV_MENU, NETWORK_MENU); - addCheckableItem(NETWORK_MENU, MenuOption::DisableNackPackets); - addCheckableItem(NETWORK_MENU, MenuOption::DisableActivityLogger, false, [](bool checked) { - UserActivityLogger::getInstance().disable(checked); - }); - addItem(NETWORK_MENU, MenuOption::CachesSize, [=] { - dialogsManager->cachesSizeDialog(); - }); - addItem(NETWORK_MENU, MenuOption::DiskCacheEditor, [=] { - dialogsManager->toggleDiskCacheEditor(); - }); - } // Developer -> Network + MenuWrapper* networkMenu = developerMenu->addMenu("Network"); + addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false); + addCheckableActionToQMenuAndActionHash(networkMenu, + MenuOption::DisableActivityLogger, + 0, + false, + &UserActivityLogger::getInstance(), + SLOT(disable(bool))); + addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0, + dialogsManager.data(), SLOT(cachesSizeDialog())); + addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0, + dialogsManager.data(), SLOT(toggleDiskCacheEditor())); - { - static const QString TIMING_MENU{ "Timing and Stats" }; - addMenu(DEV_MENU, TIMING_MENU); - { - static const QString PERF_MENU{ "Performance Timer" }; - addMenu(TIMING_MENU, PERF_MENU); - addCheckableItem(PERF_MENU, MenuOption::DisplayDebugTimingDetails); - addCheckableItem(PERF_MENU, MenuOption::OnlyDisplayTopTen, true); - addCheckableItem(PERF_MENU, MenuOption::ExpandUpdateTiming); - addCheckableItem(PERF_MENU, MenuOption::ExpandMyAvatarTiming); - addCheckableItem(PERF_MENU, MenuOption::ExpandMyAvatarSimulateTiming); - addCheckableItem(PERF_MENU, MenuOption::ExpandOtherAvatarTiming); - addCheckableItem(PERF_MENU, MenuOption::ExpandPaintGLTiming); - } - addCheckableItem(TIMING_MENU, MenuOption::TestPing, true); - addCheckableItem(TIMING_MENU, MenuOption::FrameTimer); - addItem(TIMING_MENU, MenuOption::RunTimingTests, [] { runTimingTests(); }); - addCheckableItem(TIMING_MENU, MenuOption::PipelineWarnings); - addCheckableItem(TIMING_MENU, MenuOption::SuppressShortTimings); - } // Developer -> Timing and Stats + MenuWrapper* timingMenu = developerMenu->addMenu("Timing and Stats"); + MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer"); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); - { - static const QString AUDIO_MENU{ "Audio" }; - addMenu(DEV_MENU, AUDIO_MENU); - auto audioIO = DependencyManager::get(); - addCheckableItem(AUDIO_MENU, MenuOption::AudioNoiseReduction, true, - audioIO.data(), SLOT(toggleAudioNoiseReduction())); - addCheckableItem(AUDIO_MENU, MenuOption::EchoServerAudio, false, - audioIO.data(), SLOT(toggleServerEcho())); - addCheckableItem(AUDIO_MENU, MenuOption::EchoLocalAudio, false, - audioIO.data(), SLOT(toggleLocalEcho())); - addCheckableItem(AUDIO_MENU, MenuOption::StereoAudio, false, - audioIO.data(), SLOT(toggleStereoInput())); - // Qt::CTRL | Qt::Key_M, - addCheckableItem(AUDIO_MENU, MenuOption::MuteAudio, false, - audioIO.data(), SLOT(toggleMute())); - addCheckableItem(AUDIO_MENU, MenuOption::MuteEnvironment, false, - audioIO.data(), SLOT(sendMuteEnvironmentPacket())); - { - static const QString SCOPE_MENU{ "Audio Scope" }; - addMenu(AUDIO_MENU, SCOPE_MENU); - auto scope = DependencyManager::get(); - // Qt::CTRL | Qt::Key_P - addCheckableItem(SCOPE_MENU, MenuOption::AudioScope, false, [=](bool checked) { - scope->toggle(); - }); - // Qt::CTRL | Qt::SHIFT | Qt::Key_P - addCheckableItem(SCOPE_MENU, MenuOption::AudioScopePause, false, [=](bool checked) { - scope->togglePause(); - }); - addSeparator(SCOPE_MENU, "Display Frames"); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); + addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests())); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings); - // FIXME GROUP - addCheckableItem(SCOPE_MENU, MenuOption::AudioScopeFiveFrames, true, [=](bool checked) { - scope->selectAudioScopeFiveFrames(); - }); - addCheckableItem(SCOPE_MENU, MenuOption::AudioScopeTwentyFrames, false, [=](bool checked) { - scope->selectAudioScopeTwentyFrames(); - }); - addCheckableItem(SCOPE_MENU, MenuOption::AudioScopeFiftyFrames, false, [=](bool checked) { - scope->selectAudioScopeFiftyFrames(); - }); - } - auto statsRenderer = DependencyManager::get(); + auto audioIO = DependencyManager::get(); + MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio"); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, + 0, + true, + audioIO.data(), + SLOT(toggleAudioNoiseReduction())); - // Qt::CTRL | Qt::SHIFT | Qt::Key_A, - addCheckableItem(AUDIO_MENU, MenuOption::AudioStats, false, [=](bool checked) { - statsRenderer->toggle(); - }); - addCheckableItem(AUDIO_MENU, MenuOption::AudioStatsShowInjectedStreams, false, [=](bool checked) { - statsRenderer->toggleShowInjectedStreams(); - }); - } // Developer -> Audio - } // Developer + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false, + audioIO.data(), SLOT(toggleServerEcho())); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false, + audioIO.data(), SLOT(toggleLocalEcho())); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::StereoAudio, 0, false, + audioIO.data(), SLOT(toggleStereoInput())); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteAudio, + Qt::CTRL | Qt::Key_M, + false, + audioIO.data(), + SLOT(toggleMute())); + addActionToQMenuAndActionHash(audioDebugMenu, + MenuOption::MuteEnvironment, + 0, + audioIO.data(), + SLOT(sendMuteEnvironmentPacket())); + + auto scope = DependencyManager::get(); - { - static const QString HELP_MENU{ "Help" }; - addMenu(ROOT_MENU, HELP_MENU); - addItem(HELP_MENU, MenuOption::EditEntitiesHelp, [] { - qApp->showEditEntitiesHelp(); - }); - addItem(HELP_MENU, MenuOption::AboutApp, [] { - qApp->aboutApp(); - }); - } // Help + MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); + addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, + Qt::CTRL | Qt::Key_P, false, + scope.data(), + SLOT(toggle())); + addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause, + Qt::CTRL | Qt::SHIFT | Qt::Key_P , + false, + scope.data(), + SLOT(togglePause())); + addDisabledActionAndSeparator(audioScopeMenu, "Display Frames"); + { + QAction *fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames, + 0, + true, + scope.data(), + SLOT(selectAudioScopeFiveFrames())); + + QAction *twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames, + 0, + false, + scope.data(), + SLOT(selectAudioScopeTwentyFrames())); + + QAction *fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames, + 0, + false, + scope.data(), + SLOT(selectAudioScopeFiftyFrames())); + + QActionGroup* audioScopeFramesGroup = new QActionGroup(audioScopeMenu); + audioScopeFramesGroup->addAction(fiveFrames); + audioScopeFramesGroup->addAction(twentyFrames); + audioScopeFramesGroup->addAction(fiftyFrames); } + + auto statsRenderer = DependencyManager::get(); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStats, + Qt::CTRL | Qt::SHIFT | Qt::Key_A, + false, + statsRenderer.data(), + SLOT(toggle())); + + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStatsShowInjectedStreams, + 0, + false, + statsRenderer.data(), + SLOT(toggleShowInjectedStreams())); + + MenuWrapper* helpMenu = addMenu("Help"); + addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp())); + +#ifndef Q_OS_MAC + QAction* aboutAction = helpMenu->addAction(MenuOption::AboutApp); + connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutApp())); +#endif } void Menu::loadSettings() { -// scanMenuBar(&Menu::loadAction); + scanMenuBar(&Menu::loadAction); } void Menu::saveSettings() { -// scanMenuBar(&Menu::saveAction); + scanMenuBar(&Menu::saveAction); } -void Menu::addMenuItem(const MenuItemProperties& properties) { - if (QThread::currentThread() != Application::getInstance()->getMainThread()) { - Application::getInstance()->postLambdaEvent([=]{ - addMenuItem(properties); - }); - return; - } - // Shortcut key items: in order of priority - //QString shortcutKey; - //KeyEvent shortcutKeyEvent; - //QKeySequence shortcutKeySequence; // this is what we actually use, it's set from one of the above - - //// location related items: in order of priority - //int position; - //QString beforeItem; - //QString afterItem; - - if (properties.isSeparator) { - addSeparator(properties.menuName, properties.menuItemName); - return; - } - addItem(properties.menuName, properties.menuItemName); - if (properties.isCheckable) { - setCheckable(properties.menuItemName); - if (properties.isChecked) { - setChecked(properties.menuItemName); - } - } - } - -#if 0 - void Menu::loadAction(Settings& settings, QAction& action) { if (action.isChecked() != settings.value(action.text(), action.isChecked()).toBool()) { action.trigger(); @@ -690,7 +594,7 @@ void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& setting settings.endGroup(); } -void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName, int menuItemLocation) { +void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, int menuItemLocation) { QAction* actionBefore = NULL; if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { actionBefore = destinationMenu->actions()[menuItemLocation]; @@ -710,5 +614,406 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& } } +QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut, + const QObject* receiver, + const char* member, + QAction::MenuRole role, + int menuItemLocation) { + QAction* action = NULL; + QAction* actionBefore = NULL; -#endif + if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { + actionBefore = destinationMenu->actions()[menuItemLocation]; + } + + if (!actionBefore) { + if (receiver && member) { + action = destinationMenu->addAction(actionName, receiver, member, shortcut); + } else { + action = destinationMenu->addAction(actionName); + action->setShortcut(shortcut); + } + } else { + action = new QAction(actionName, destinationMenu); + action->setShortcut(shortcut); + destinationMenu->insertAction(actionBefore, action); + + if (receiver && member) { + connect(action, SIGNAL(triggered()), receiver, member); + } + } + action->setMenuRole(role); + + _actionHash.insert(actionName, action); + + return action; +} + +QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + QAction* action, + const QString& actionName, + const QKeySequence& shortcut, + QAction::MenuRole role, + int menuItemLocation) { + QAction* actionBefore = NULL; + + if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { + actionBefore = destinationMenu->actions()[menuItemLocation]; + } + + if (!actionName.isEmpty()) { + action->setText(actionName); + } + + if (shortcut != 0) { + action->setShortcut(shortcut); + } + + if (role != QAction::NoRole) { + action->setMenuRole(role); + } + + if (!actionBefore) { + destinationMenu->addAction(action); + } else { + destinationMenu->insertAction(actionBefore, action); + } + + _actionHash.insert(action->text(), action); + + return action; +} + +QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut, + const bool checked, + const QObject* receiver, + const char* member, + int menuItemLocation) { + + QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member, + QAction::NoRole, menuItemLocation); + action->setCheckable(true); + action->setChecked(checked); + + return action; +} + +void Menu::removeAction(MenuWrapper* menu, const QString& actionName) { + menu->removeAction(_actionHash.value(actionName)); + _actionHash.remove(actionName); +} + +void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) { + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, + Q_ARG(const QString&, menuOption), + Q_ARG(bool, isChecked)); + return; + } + QAction* menu = _actionHash.value(menuOption); + if (menu) { + menu->setChecked(isChecked); + } +} + +bool Menu::isOptionChecked(const QString& menuOption) const { + const QAction* menu = _actionHash.value(menuOption); + if (menu) { + return menu->isChecked(); + } + return false; +} + +void Menu::triggerOption(const QString& menuOption) { + QAction* action = _actionHash.value(menuOption); + if (action) { + action->trigger(); + } else { + qCDebug(interfaceapp) << "NULL Action for menuOption '" << menuOption << "'"; + } +} + +QAction* Menu::getActionForOption(const QString& menuOption) { + return _actionHash.value(menuOption); +} + +QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) { + QList menuActions; + if (menu) { + menuActions = menu->actions(); + } else { + menuActions = actions(); + } + + foreach (QAction* menuAction, menuActions) { + QString actionText = menuAction->text(); + if (menuName == menuAction->text()) { + return menuAction; + } + } + return NULL; +} + +MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) { + QAction* action = getActionFromName(menuName, menu); + if (action) { + return MenuWrapper::fromMenu(action->menu()); + } + return NULL; +} + +MenuWrapper* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* parent = NULL; + MenuWrapper* menu = NULL; + foreach (QString menuTreePart, menuTree) { + parent = menu; + finalMenuPart = menuTreePart.trimmed(); + menu = getSubMenuFromName(finalMenuPart, parent); + if (!menu) { + break; + } + } + return parent; +} + +MenuWrapper* Menu::getMenu(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* parent = NULL; + MenuWrapper* menu = NULL; + int item = 0; + foreach (QString menuTreePart, menuTree) { + menu = getSubMenuFromName(menuTreePart.trimmed(), parent); + if (!menu) { + break; + } + parent = menu; + item++; + } + return menu; +} + +QAction* Menu::getMenuAction(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* parent = NULL; + QAction* action = NULL; + foreach (QString menuTreePart, menuTree) { + action = getActionFromName(menuTreePart.trimmed(), parent); + if (!action) { + break; + } + parent = MenuWrapper::fromMenu(action->menu()); + } + return action; +} + +int Menu::findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem) { + int position = 0; + foreach(QAction* action, menu->actions()) { + if (action->text() == searchMenuItem) { + return position; + } + position++; + } + return UNSPECIFIED_POSITION; // not found +} + +int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition) { + QList menuActions = menu->actions(); + if (requestedPosition > 1 && requestedPosition < menuActions.size()) { + QAction* beforeRequested = menuActions[requestedPosition - 1]; + if (beforeRequested->isSeparator()) { + requestedPosition--; + } + } + return requestedPosition; +} + + +MenuWrapper* Menu::addMenu(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* addTo = NULL; + MenuWrapper* menu = NULL; + foreach (QString menuTreePart, menuTree) { + menu = getSubMenuFromName(menuTreePart.trimmed(), addTo); + if (!menu) { + if (!addTo) { + menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed())); + } else { + menu = addTo->addMenu(menuTreePart.trimmed()); + } + } + addTo = menu; + } + + QMenuBar::repaint(); + return menu; +} + +void Menu::removeMenu(const QString& menuName) { + QAction* action = getMenuAction(menuName); + + // only proceed if the menu actually exists + if (action) { + QString finalMenuPart; + MenuWrapper* parent = getMenuParent(menuName, finalMenuPart); + if (parent) { + parent->removeAction(action); + } else { + QMenuBar::removeAction(action); + } + + QMenuBar::repaint(); + } +} + +bool Menu::menuExists(const QString& menuName) { + QAction* action = getMenuAction(menuName); + + // only proceed if the menu actually exists + if (action) { + return true; + } + return false; +} + +void Menu::addSeparator(const QString& menuName, const QString& separatorName) { + MenuWrapper* menuObj = getMenu(menuName); + if (menuObj) { + addDisabledActionAndSeparator(menuObj, separatorName); + } +} + +void Menu::removeSeparator(const QString& menuName, const QString& separatorName) { + MenuWrapper* menu = getMenu(menuName); + bool separatorRemoved = false; + if (menu) { + int textAt = findPositionOfMenuItem(menu, separatorName); + QList menuActions = menu->actions(); + QAction* separatorText = menuActions[textAt]; + if (textAt > 0 && textAt < menuActions.size()) { + QAction* separatorLine = menuActions[textAt - 1]; + if (separatorLine) { + if (separatorLine->isSeparator()) { + menu->removeAction(separatorText); + menu->removeAction(separatorLine); + separatorRemoved = true; + } + } + } + } + if (separatorRemoved) { + QMenuBar::repaint(); + } +} + +void Menu::addMenuItem(const MenuItemProperties& properties) { + MenuWrapper* menuObj = getMenu(properties.menuName); + if (menuObj) { + QShortcut* shortcut = NULL; + if (!properties.shortcutKeySequence.isEmpty()) { + shortcut = new QShortcut(properties.shortcutKeySequence, this); + } + + // check for positioning requests + int requestedPosition = properties.position; + if (requestedPosition == UNSPECIFIED_POSITION && !properties.beforeItem.isEmpty()) { + requestedPosition = findPositionOfMenuItem(menuObj, properties.beforeItem); + // double check that the requested location wasn't a separator label + requestedPosition = positionBeforeSeparatorIfNeeded(menuObj, requestedPosition); + } + if (requestedPosition == UNSPECIFIED_POSITION && !properties.afterItem.isEmpty()) { + int afterPosition = findPositionOfMenuItem(menuObj, properties.afterItem); + if (afterPosition != UNSPECIFIED_POSITION) { + requestedPosition = afterPosition + 1; + } + } + + QAction* menuItemAction = NULL; + if (properties.isSeparator) { + addDisabledActionAndSeparator(menuObj, properties.menuItemName, requestedPosition); + } else if (properties.isCheckable) { + menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName, + properties.shortcutKeySequence, properties.isChecked, + MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), requestedPosition); + } else { + menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence, + MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), + QAction::NoRole, requestedPosition); + } + if (shortcut && menuItemAction) { + connect(shortcut, SIGNAL(activated()), menuItemAction, SLOT(trigger())); + } + QMenuBar::repaint(); + } +} + +void Menu::removeMenuItem(const QString& menu, const QString& menuitem) { + MenuWrapper* menuObj = getMenu(menu); + if (menuObj) { + removeAction(menuObj, menuitem); + QMenuBar::repaint(); + } +}; + +bool Menu::menuItemExists(const QString& menu, const QString& menuitem) { + QAction* menuItemAction = _actionHash.value(menuitem); + if (menuItemAction) { + return (getMenu(menu) != NULL); + } + return false; +}; + + +MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) { + VrMenu::instance()->addMenu(menu); + _backMap[menu] = this; +} + +QList MenuWrapper::actions() { + return _realMenu->actions(); +} + +MenuWrapper* MenuWrapper::addMenu(const QString& menuName) { + return new MenuWrapper(_realMenu->addMenu(menuName)); +} + +void MenuWrapper::setEnabled(bool enabled) { + _realMenu->setEnabled(enabled); +} + +void MenuWrapper::addSeparator() { + _realMenu->addSeparator(); +} + +void MenuWrapper::addAction(QAction* action) { + _realMenu->addAction(action); + VrMenu::instance()->addAction(_realMenu, action); +} + +QAction* MenuWrapper::addAction(const QString& menuName) { + QAction* action = _realMenu->addAction(menuName); + VrMenu::instance()->addAction(_realMenu, action); + return action; +} + +QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) { + QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut); + VrMenu::instance()->addAction(_realMenu, action); + return action; +} + +void MenuWrapper::removeAction(QAction* action) { + _realMenu->removeAction(action); +} + +void MenuWrapper::insertAction(QAction* before, QAction* action) { + _realMenu->insertAction(before, action); + VrMenu::instance()->insertAction(before, action); +} + +QHash MenuWrapper::_backMap; diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d23d7658d0..194f64ddc7 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -18,77 +18,114 @@ #include #include #include -#include + #include -#include - #include "DiscoverabilityManager.h" -#include class Settings; -// Proxy object to simplify porting over -class HifiAction { - const QString _menuOption; +class MenuWrapper : public QObject { public: - HifiAction(const QString & menuOption); - void setCheckable(bool); - void setChecked(bool); - void setVisible(bool); - QString shortcut() const; - void setText(const QString &); - void setTriggerAction(std::function); - void setToggleAction(std::function); + QList actions(); + MenuWrapper* addMenu(const QString& menuName); + void setEnabled(bool enabled = true); + void addSeparator(); + void addAction(QAction* action); + + QAction* addAction(const QString& menuName); + void insertAction(QAction* before, QAction* menuName); + + QAction* addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); + void removeAction(QAction* action); + + QAction* newAction() { + return new QAction(_realMenu); + } +private: + MenuWrapper(QMenu* menu); + + static MenuWrapper* fromMenu(QMenu* menu) { + return _backMap[menu]; + } + + QMenu* const _realMenu; + static QHash _backMap; + friend class Menu; }; -class Menu : public HifiMenu { +class Menu : public QMenuBar { Q_OBJECT - public: - Menu(QQuickItem * parent = 0); static Menu* getInstance(); - - // Override the base type HifiMenu with this class instead - static void registerType() { - qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); - } - + void loadSettings(); void saveSettings(); + + MenuWrapper* getMenu(const QString& menuName); - HifiAction * getActionForOption(const QString& menuOption) { - return new HifiAction(menuOption); - } - + void triggerOption(const QString& menuOption); + QAction* getActionForOption(const QString& menuOption); + + QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut = 0, + const QObject* receiver = NULL, + const char* member = NULL, + QAction::MenuRole role = QAction::NoRole, + int menuItemLocation = UNSPECIFIED_POSITION); + QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + QAction* action, + const QString& actionName = QString(), + const QKeySequence& shortcut = 0, + QAction::MenuRole role = QAction::NoRole, + int menuItemLocation = UNSPECIFIED_POSITION); + + void removeAction(MenuWrapper* menu, const QString& actionName); + +public slots: + MenuWrapper* addMenu(const QString& menuName); + void removeMenu(const QString& menuName); + bool menuExists(const QString& menuName); + void addSeparator(const QString& menuName, const QString& separatorName); + void removeSeparator(const QString& menuName, const QString& separatorName); void addMenuItem(const MenuItemProperties& properties); - - bool isOptionChecked(const QString& menuOption) const { - return HifiMenu::isChecked(menuOption); - } - - void setIsOptionChecked(const QString& menuOption, bool isChecked) { - HifiMenu::setChecked(menuOption, isChecked); - } - - void triggerOption(const QString& menuOption) { - HifiMenu::triggerItem(menuOption); - } - - void setOptionText(const QString& menuOption, const QString & text) { - HifiMenu::setItemText(menuOption, text); - } - - void setOptionTriggerAction(const QString& menuOption, std::function f) { - HifiMenu::setTriggerAction(menuOption, f); - } - -private: - void init(); - + void removeMenuItem(const QString& menuName, const QString& menuitem); + bool menuItemExists(const QString& menuName, const QString& menuitem); + bool isOptionChecked(const QString& menuOption) const; + void setIsOptionChecked(const QString& menuOption, bool isChecked); + private: static Menu* _instance; - friend class HifiAction; + Menu(); + + typedef void(*settingsAction)(Settings&, QAction&); + static void loadAction(Settings& settings, QAction& action); + static void saveAction(Settings& settings, QAction& action); + void scanMenuBar(settingsAction modifySetting); + void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings); + + /// helper method to have separators with labels that are also compatible with OS X + void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, + int menuItemLocation = UNSPECIFIED_POSITION); + + QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut = 0, + const bool checked = false, + const QObject* receiver = NULL, + const char* member = NULL, + int menuItemLocation = UNSPECIFIED_POSITION); + + QAction* getActionFromName(const QString& menuName, MenuWrapper* menu); + MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu); + MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart); + + QAction* getMenuAction(const QString& menuName); + int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem); + int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition); + + QHash _actionHash; }; namespace MenuOption { @@ -243,4 +280,5 @@ namespace MenuOption { const QString VisibleToNoOne = "No one"; const QString Wireframe = "Wireframe"; } + #endif // hifi_Menu_h diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 043d5bf40d..7d719873f4 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -216,7 +216,7 @@ void OculusManager::connect() { _isConnected = false; // we're definitely not in "VR mode" so tell the menu that - Menu::getInstance()->setIsOptionChecked(MenuOption::EnableVRMode, false); + Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false); } } diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index 823bb9f8d0..277c611f04 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -28,50 +28,72 @@ void MenuScriptingInterface::menuItemTriggered() { } void MenuScriptingInterface::addMenu(const QString& menu) { - Menu::getInstance()->addMenu("", menu); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenu", Q_ARG(const QString&, menu)); } void MenuScriptingInterface::removeMenu(const QString& menu) { - Menu::getInstance()->removeMenu(menu); + QMetaObject::invokeMethod(Menu::getInstance(), "removeMenu", Q_ARG(const QString&, menu)); } bool MenuScriptingInterface::menuExists(const QString& menu) { - return Menu::getInstance()->menuExists(menu); + bool result; + QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(const QString&, menu)); + return result; } void MenuScriptingInterface::addSeparator(const QString& menuName, const QString& separatorName) { - Menu::getInstance()->addSeparator(menuName, separatorName); + QMetaObject::invokeMethod(Menu::getInstance(), "addSeparator", + Q_ARG(const QString&, menuName), + Q_ARG(const QString&, separatorName)); } void MenuScriptingInterface::removeSeparator(const QString& menuName, const QString& separatorName) { - Menu::getInstance()->removeSeparator(menuName, separatorName); + QMetaObject::invokeMethod(Menu::getInstance(), "removeSeparator", + Q_ARG(const QString&, menuName), + Q_ARG(const QString&, separatorName)); } void MenuScriptingInterface::addMenuItem(const MenuItemProperties& properties) { - Menu::getInstance()->addMenuItem(properties); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); } void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem, const QString& shortcutKey) { - Menu::getInstance()->addItem(menu, menuitem); - Menu::getInstance()->setItemShortcut(menuitem, shortcutKey); + MenuItemProperties properties(menu, menuitem, shortcutKey); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); } void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem) { - Menu::getInstance()->addItem(menu, menuitem); + MenuItemProperties properties(menu, menuitem); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); } void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& menuitem) { - Menu::getInstance()->removeItem(menuitem); + QMetaObject::invokeMethod(Menu::getInstance(), "removeMenuItem", + Q_ARG(const QString&, menu), + Q_ARG(const QString&, menuitem)); }; bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) { - return Menu::getInstance()->itemExists(menu, menuitem); + bool result; + QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(const QString&, menu), + Q_ARG(const QString&, menuitem)); + return result; } bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { - return Menu::getInstance()->isChecked(menuOption); + bool result; + QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(const QString&, menuOption)); + return result; } void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) { - Menu::getInstance()->setChecked(menuOption, isChecked); + QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, + Q_ARG(const QString&, menuOption), + Q_ARG(bool, isChecked)); } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 5e747f216d..bd2903863d 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -45,6 +45,7 @@ QScriptValue WindowScriptingInterface::hasFocus() { } void WindowScriptingInterface::setFocus() { + // It's forbidden to call focus() from another thread. Application::getInstance()->postLambdaEvent([] { auto window = Application::getInstance()->getWindow(); window->activateWindow(); diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index cedb26fd9b..4a899a641e 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -8,7 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include #include #include diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 5922d9f3bb..b452f153f0 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -25,18 +25,19 @@ LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), _root } void LoginDialog::toggleAction() { - AccountManager & accountManager = AccountManager::getInstance(); - Menu* menu = Menu::getInstance(); + AccountManager& accountManager = AccountManager::getInstance(); + QAction* loginAction = Menu::getInstance()->getActionForOption(MenuOption::Login); + Q_CHECK_PTR(loginAction); + disconnect(loginAction, 0, 0, 0); + if (accountManager.isLoggedIn()) { // change the menu item to logout - menu->setOptionText(MenuOption::Login, "Logout " + accountManager.getAccountInfo().getUsername()); - menu->setOptionTriggerAction(MenuOption::Login, [] { - AccountManager::getInstance().logout(); - }); + loginAction->setText("Logout " + accountManager.getAccountInfo().getUsername()); + connect(loginAction, &QAction::triggered, &accountManager, &AccountManager::logout); } else { // change the menu item to login - menu->setOptionText(MenuOption::Login, "Login"); - menu->setOptionTriggerAction(MenuOption::Login, [] { + loginAction->setText("Login"); + connect(loginAction, &QAction::triggered, [] { LoginDialog::show(); }); } diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 4c8d29d84c..e1fb0ce481 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -44,9 +44,9 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : connect(&_scriptsModelFilter, &QSortFilterProxyModel::modelReset, this, &RunningScriptsWidget::selectFirstInList); - // FIXME - // QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText); - // ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts."); + // FIXME: menu isn't prepared at this point. + //QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText); + //ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts."); _scriptsModelFilter.setSourceModel(&_scriptsModel); _scriptsModelFilter.sort(0, Qt::AscendingOrder); @@ -162,7 +162,8 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) { QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry(); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); - int topMargin = titleBarHeight; + int menuBarHeight = Menu::getInstance()->geometry().height(); + int topMargin = titleBarHeight + menuBarHeight; setGeometry(parentGeometry.topLeft().x(), parentGeometry.topLeft().y() + topMargin, size().width(), parentWidget()->height() - topMargin); diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index 4edae4f2a4..5888b83f4a 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -38,7 +38,8 @@ bool ToolWindow::event(QEvent* event) { QRect mainGeometry = mainWindow->geometry(); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); - int topMargin = titleBarHeight; + int menuBarHeight = Menu::getInstance()->geometry().height(); + int topMargin = titleBarHeight + menuBarHeight; _lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin, DEFAULT_WIDTH, mainGeometry.height() - topMargin); diff --git a/libraries/ui/src/HifiMenu.cpp b/libraries/ui/src/HifiMenu.cpp index 88c9b48f43..f677fe0aef 100644 --- a/libraries/ui/src/HifiMenu.cpp +++ b/libraries/ui/src/HifiMenu.cpp @@ -1,47 +1,64 @@ #include "HifiMenu.h" #include +#include -// FIXME can this be made a class member? -static const QString MENU_SUFFIX{ "__Menu" }; +// Binds together a Qt Action or Menu with the QML Menu or MenuItem +class MenuUserData : public QObjectUserData { + static const int USER_DATA_ID; -HIFI_QML_DEF_LAMBDA(HifiMenu, [=](QQmlContext* context, QObject* newItem) { +public: + MenuUserData(QAction* action, QObject* qmlObject) { + init(action, qmlObject); + } + MenuUserData(QMenu* menu, QObject* qmlObject) { + init(menu, qmlObject); + } + + const QUuid uuid{ QUuid::createUuid() }; + + static MenuUserData* forObject(QObject* object) { + return static_cast(object->userData(USER_DATA_ID)); + } + +private: + MenuUserData(const MenuUserData&); + + void init(QObject* widgetObject, QObject* qmlObject) { + widgetObject->setUserData(USER_DATA_ID, this); + qmlObject->setUserData(USER_DATA_ID, this); + qmlObject->setObjectName(uuid.toString()); + // Make sure we can find it again in the future + Q_ASSERT(VrMenu::instance()->findMenuObject(uuid.toString())); + } +}; + +const int MenuUserData::USER_DATA_ID = QObject::registerUserData(); + +HIFI_QML_DEF_LAMBDA(VrMenu, [&](QQmlContext* context, QObject* newItem) { auto offscreenUi = DependencyManager::get(); QObject * rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); Q_ASSERT(rootMenu); - static_cast(newItem)->setRootMenu(rootMenu); + static_cast(newItem)->setRootMenu(rootMenu); context->setContextProperty("rootMenu", rootMenu); }); -HifiMenu::HifiMenu(QQuickItem* parent) : QQuickItem(parent), _triggerMapper(this), _toggleMapper(this) { +VrMenu* VrMenu::_instance{ nullptr }; + +VrMenu* VrMenu::instance() { + if (!_instance) { + VrMenu::registerType(); + VrMenu::load(); + Q_ASSERT(_instance); + } + return _instance; +} + +VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) { + _instance = this; this->setEnabled(false); - connect(&_triggerMapper, SIGNAL(mapped(QString)), this, SLOT(onTriggeredByName(const QString &))); - connect(&_toggleMapper, SIGNAL(mapped(QString)), this, SLOT(onToggledByName(const QString &))); -} - -void HifiMenu::onTriggeredByName(const QString & name) { - qDebug() << name << " triggered"; - if (_triggerActions.count(name)) { - _triggerActions[name](); - } -} - -void HifiMenu::onToggledByName(const QString & name) { - qDebug() << name << " toggled"; - if (_toggleActions.count(name)) { - QObject* menu = findMenuObject(name); - bool checked = menu->property("checked").toBool(); - _toggleActions[name](checked); - } -} - -void HifiMenu::setToggleAction(const QString & name, std::function f) { - _toggleActions[name] = f; -} - -void HifiMenu::setTriggerAction(const QString & name, std::function f) { - _triggerActions[name] = f; } +// QML helper functions QObject* addMenu(QObject* parent, const QString & text) { // FIXME add more checking here to ensure no name conflicts QVariant returnedValue; @@ -50,7 +67,7 @@ QObject* addMenu(QObject* parent, const QString & text) { Q_ARG(QVariant, text)); QObject* result = returnedValue.value(); if (result) { - result->setObjectName(text + MENU_SUFFIX); + result->setObjectName(text); } return result; } @@ -67,236 +84,109 @@ QObject* addItem(QObject* parent, const QString& text) { return result; } -const QObject* HifiMenu::findMenuObject(const QString & menuOption) const { +const QObject* VrMenu::findMenuObject(const QString & menuOption) const { if (menuOption.isEmpty()) { return _rootMenu; } - const QObject* result = _rootMenu->findChild(menuOption + MENU_SUFFIX); + const QObject* result = _rootMenu->findChild(menuOption); return result; } -QObject* HifiMenu::findMenuObject(const QString & menuOption) { +QObject* VrMenu::findMenuObject(const QString & menuOption) { if (menuOption.isEmpty()) { return _rootMenu; } - QObject* result = _rootMenu->findChild(menuOption + MENU_SUFFIX); + QObject* result = _rootMenu->findChild(menuOption); return result; } -void HifiMenu::addMenu(const QString & parentMenu, const QString & menuOption) { - QObject* parent = findMenuObject(parentMenu); - QObject* result = ::addMenu(parent, menuOption); - Q_ASSERT(result); - result->setObjectName(menuOption + MENU_SUFFIX); - Q_ASSERT(findMenuObject(menuOption)); -} - -void HifiMenu::removeMenu(const QString& menuName) { - QObject* menu = findMenuObject(menuName); - Q_ASSERT(menu); - Q_ASSERT(menu != _rootMenu); - QMetaObject::invokeMethod(menu->parent(), "removeItem", - Q_ARG(QVariant, QVariant::fromValue(menu))); -} - -bool HifiMenu::menuExists(const QString& menuName) const { - return findMenuObject(menuName); -} - -void HifiMenu::addSeparator(const QString& parentMenu, const QString& separatorName) { - QObject * parent = findMenuObject(parentMenu); - bool invokeResult = QMetaObject::invokeMethod(parent, "addSeparator", Qt::DirectConnection); - Q_ASSERT(invokeResult); - addItem(parentMenu, separatorName); - enableItem(separatorName, false); -} - -void HifiMenu::removeSeparator(const QString& parentMenu, const QString& separatorName) { -} - -void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption) { - QObject* parent = findMenuObject(parentMenu); - Q_ASSERT(parent); - QObject* result = ::addItem(parent, menuOption); - Q_ASSERT(result); - result->setObjectName(menuOption + MENU_SUFFIX); - Q_ASSERT(findMenuObject(menuOption)); - - _triggerMapper.setMapping(result, menuOption); - connect(result, SIGNAL(triggered()), &_triggerMapper, SLOT(map())); - - _toggleMapper.setMapping(result, menuOption); - connect(result, SIGNAL(toggled(bool)), &_toggleMapper, SLOT(map())); -} - -void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, std::function f) { - setTriggerAction(menuOption, f); - addItem(parentMenu, menuOption); -} - -void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, QObject* receiver, const char* slot) { - addItem(parentMenu, menuOption); - connectItem(menuOption, receiver, slot); -} - -void HifiMenu::removeItem(const QString& menuOption) { - removeMenu(menuOption); -} - -bool HifiMenu::itemExists(const QString& menuName, const QString& menuitem) const { - return findMenuObject(menuName); -} - -void HifiMenu::triggerItem(const QString& menuOption) { - QObject* menuItem = findMenuObject(menuOption); - Q_ASSERT(menuItem); - Q_ASSERT(menuItem != _rootMenu); - QMetaObject::invokeMethod(menuItem, "trigger"); -} - -QHash warned; -void warn(const QString & menuOption) { - if (!warned.contains(menuOption)) { - warned[menuOption] = menuOption; - qWarning() << "No menu item: " << menuOption; - } -} - -bool HifiMenu::isChecked(const QString& menuOption) const { - const QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return false; - } - return menuItem->property("checked").toBool(); -} - -void HifiMenu::setChecked(const QString& menuOption, bool isChecked) { - QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return; - } - if (menuItem->property("checked").toBool() != isChecked) { - menuItem->setProperty("checked", QVariant::fromValue(isChecked)); - Q_ASSERT(menuItem->property("checked").toBool() == isChecked); - } -} - -void HifiMenu::setCheckable(const QString& menuOption, bool checkable) { - QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return; - } - - menuItem->setProperty("checkable", QVariant::fromValue(checkable)); - Q_ASSERT(menuItem->property("checkable").toBool() == checkable); -} - -void HifiMenu::setItemText(const QString& menuOption, const QString& text) { - QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return; - } - if (menuItem->property("type").toInt() == 2) { - menuItem->setProperty("title", QVariant::fromValue(text)); - } else { - menuItem->setProperty("text", QVariant::fromValue(text)); - } -} - -void HifiMenu::setRootMenu(QObject* rootMenu) { +void VrMenu::setRootMenu(QObject* rootMenu) { _rootMenu = rootMenu; } -void HifiMenu::enableItem(const QString & menuOption, bool enabled) { - QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return; +void VrMenu::addMenu(QMenu* menu) { + Q_ASSERT(!MenuUserData::forObject(menu)); + QObject * parent = menu->parent(); + QObject * qmlParent; + if (dynamic_cast(parent)) { + MenuUserData* userData = MenuUserData::forObject(parent); + qmlParent = findMenuObject(userData->uuid.toString()); + } else if (dynamic_cast(parent)) { + qmlParent = _rootMenu; + } else { + Q_ASSERT(false); } - menuItem->setProperty("enabled", QVariant::fromValue(enabled)); + QObject* result = ::addMenu(qmlParent, menu->title()); + new MenuUserData(menu, result); } -void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked) { - addItem(parentMenu, menuOption); - setCheckable(menuOption); - if (checked) { - setChecked(menuOption, checked); - } +void updateQmlItemFromAction(QObject* target, QAction* source) { + target->setProperty("checkable", source->isCheckable()); + target->setProperty("enabled", source->isEnabled()); + target->setProperty("visible", source->isVisible()); + target->setProperty("text", source->text()); + target->setProperty("checked", source->isChecked()); } -void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f) { - setToggleAction(menuOption, f); - addCheckableItem(parentMenu, menuOption, checked); +void bindActionToQmlAction(QObject* qmlAction, QAction* action) { + new MenuUserData(action, qmlAction); + updateQmlItemFromAction(qmlAction, action); + QObject::connect(action, &QAction::changed, [=] { + updateQmlItemFromAction(qmlAction, action); + }); + QObject::connect(action, &QAction::toggled, [=](bool checked) { + qmlAction->setProperty("checked", checked); + }); + QObject::connect(qmlAction, SIGNAL(triggered()), action, SLOT(trigger())); } -void HifiMenu::setItemVisible(const QString& menuOption, bool visible) { - QObject* result = findMenuObject(menuOption); - if (result) { - result->setProperty("visible", visible); - } -} - -bool HifiMenu::isItemVisible(const QString& menuOption) { - QObject* result = findMenuObject(menuOption); - if (result) { - return result->property("visible").toBool(); - } - return false; -} - -void HifiMenu::setItemShortcut(const QString& menuOption, const QString& shortcut) { - QObject* result = findMenuObject(menuOption); - if (result) { - result->setProperty("shortcut", shortcut); - } -} - -QString HifiMenu::getItemShortcut(const QString& menuOption) { - QObject* result = findMenuObject(menuOption); - if (result) { - return result->property("shortcut").toString(); - } - return QString(); -} - -void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, QObject* receiver, const char* slot) { - addCheckableItem(parentMenu, menuOption, checked); - connectItem(menuOption, receiver, slot); -} - -void HifiMenu::connectCheckable(const QString& menuOption, QObject* receiver, const char* slot) { - QObject* result = findMenuObject(menuOption); - connect(result, SIGNAL(toggled(bool)), receiver, slot); -} - -void HifiMenu::connectItem(const QString& menuOption, QObject* receiver, const char* slot) { - QObject* result = findMenuObject(menuOption); - connect(result, SIGNAL(triggered()), receiver, slot); -} - -void HifiMenu::setExclusiveGroup(const QString& menuOption, const QString& groupName) { - static const QString GROUP_SUFFIX{ "__Group" }; - auto offscreenUi = DependencyManager::get(); - QObject* group = offscreenUi->getRootItem()->findChild(groupName + GROUP_SUFFIX); - if (!group) { - group = offscreenUi->load("ExclusiveGroup.qml"); - Q_ASSERT(group); - } - QObject* menuItem = findMenuObject(menuOption); - bool result = menuItem->setProperty("text", QVariant::fromValue(group)); +void VrMenu::addAction(QMenu* menu, QAction* action) { + Q_ASSERT(!MenuUserData::forObject(action)); + Q_ASSERT(MenuUserData::forObject(menu)); + MenuUserData* userData = MenuUserData::forObject(menu); + QObject* parent = findMenuObject(userData->uuid.toString()); + Q_ASSERT(parent); + QObject* result = ::addItem(parent, action->text()); Q_ASSERT(result); + // Bind the QML and Widget together + bindActionToQmlAction(result, action); } -bool HifiMenu::connectAction(int action, QObject * receiver, const char * slot) { - auto offscreenUi = DependencyManager::get(); - QObject* rootMenu = offscreenUi->getRootItem()->findChild("AllActions"); - QString name = "HifiAction_" + QVariant(action).toString(); - QObject* quitAction = rootMenu->findChild(name); - connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); - return true; +void VrMenu::insertAction(QAction* before, QAction* action) { + QObject* beforeQml{ nullptr }; + { + MenuUserData* beforeUserData = MenuUserData::forObject(before); + Q_ASSERT(beforeUserData); + beforeQml = findMenuObject(beforeUserData->uuid.toString()); + } + + QObject* menu = beforeQml->parent(); + int index{ -1 }; + QVariant itemsVar = menu->property("items"); + QList items = itemsVar.toList(); + // FIXME add more checking here to ensure no name conflicts + for (index = 0; index < items.length(); ++index) { + QObject* currentQmlItem = items.at(index).value(); + if (currentQmlItem == beforeQml) { + break; + } + } + + QObject* result{ nullptr }; + if (index < 0 || index >= items.length()) { + result = ::addItem(menu, action->text()); + } else { + QQuickMenuItem* returnedValue{ nullptr }; + bool invokeResult = QMetaObject::invokeMethod(menu, "insertItem", Qt::DirectConnection, + Q_RETURN_ARG(QQuickMenuItem*, returnedValue), + Q_ARG(int, index), Q_ARG(QString, action->text())); + Q_ASSERT(invokeResult); + result = reinterpret_cast(returnedValue); + } + Q_ASSERT(result); + bindActionToQmlAction(result, action); } +void VrMenu::removeAction(QAction* action) { + // FIXME implement +} diff --git a/libraries/ui/src/HifiMenu.h b/libraries/ui/src/HifiMenu.h index 501baa3cc0..c8e555d464 100644 --- a/libraries/ui/src/HifiMenu.h +++ b/libraries/ui/src/HifiMenu.h @@ -16,67 +16,32 @@ #include #include #include +#include +#include #include "OffscreenUi.h" -class HifiMenu : public QQuickItem { +class VrMenu : public QQuickItem { Q_OBJECT HIFI_QML_DECL_LAMBDA public: - - static bool connectAction(int action, QObject * receiver, const char * slot); - - HifiMenu(QQuickItem* parent = nullptr); - - void setToggleAction(const QString& name, std::function f); - void setTriggerAction(const QString& name, std::function f); - - void addMenu(const QString& parentMenu, const QString& menuOption); - void removeMenu(const QString& menuName); - bool menuExists(const QString& menuName) const; - - void addSeparator(const QString& menuName, const QString& separatorName); - void removeSeparator(const QString& menuName, const QString& separatorName); - - void addItem(const QString& parentMenu, const QString& menuOption); - void addItem(const QString& parentMenu, const QString& menuOption, std::function f); - void addItem(const QString& parentMenu, const QString& menuOption, QObject* receiver, const char* slot); - - void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked = false); - void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f); - void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, QObject* receiver, const char* slot); - void connectCheckable(const QString& menuOption, QObject* receiver, const char* slot); - void connectItem(const QString& menuOption, QObject* receiver, const char* slot); - - void removeItem(const QString& menuitem); - bool itemExists(const QString& menuName, const QString& menuitem) const; - void triggerItem(const QString& menuOption); - void enableItem(const QString& menuOption, bool enabled = true); - bool isChecked(const QString& menuOption) const; - void setChecked(const QString& menuOption, bool checked = true); - void setCheckable(const QString& menuOption, bool checkable = true); - void setExclusiveGroup(const QString& menuOption, const QString& groupName); - void setItemText(const QString& menuOption, const QString& text); - void setItemVisible(const QString& menuOption, bool visible = true); - bool isItemVisible(const QString& menuOption); - - void setItemShortcut(const QString& menuOption, const QString& shortcut); - QString getItemShortcut(const QString& menuOption); + static VrMenu* instance(); + VrMenu(QQuickItem* parent = nullptr); + void addMenu(QMenu* menu); + void addAction(QMenu* parent, QAction* action); + void insertAction(QAction* before, QAction* action); + void removeAction(QAction* action); void setRootMenu(QObject* rootMenu); -private slots: - void onTriggeredByName(const QString& name); - void onToggledByName(const QString& name); - protected: - QHash> _triggerActions; - QHash> _toggleActions; + QObject* _rootMenu{ nullptr }; + QObject* findMenuObject(const QString& name); const QObject* findMenuObject(const QString& name) const; - QObject* _rootMenu{ nullptr }; - QSignalMapper _triggerMapper; - QSignalMapper _toggleMapper; + + static VrMenu* _instance; + friend class MenuUserData; }; #endif // hifi_MenuConstants_h diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 052a6aac4e..563beb0cc8 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -366,10 +366,14 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { QMouseEvent* mouseEvent = static_cast(event); QPointF originalPos = mouseEvent->localPos(); QPointF transformedPos = _mouseTranslator(originalPos); + transformedPos = mapWindowToUi(transformedPos, originalDestination); QMouseEvent mappedEvent(mouseEvent->type(), - mapWindowToUi(transformedPos, originalDestination), + transformedPos, mouseEvent->screenPos(), mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers()); + if (event->type() == QEvent::MouseMove) { + _qmlEngine->rootContext()->setContextProperty("lastMousePosition", transformedPos); + } mappedEvent.ignore(); if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { return mappedEvent.isAccepted(); @@ -429,6 +433,7 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::functionfindChild(name); } if (item) { + qDebug() << "Turning item " << !item->isEnabled(); item->setEnabled(!item->isEnabled()); } } diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 37e229d571..192bfd928b 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -320,7 +320,7 @@ public: glDisable(GL_DEPTH_TEST); MessageDialog::registerType(); - HifiMenu::registerType(); + VrMenu::registerType(); qmlRegisterType("Hifi", 1, 0, "MenuConstants"); @@ -348,10 +348,9 @@ public: #else offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); offscreenUi->load(QUrl("TestRoot.qml")); - offscreenUi->load(QUrl("Menu.qml")); + offscreenUi->load(QUrl("InterfaceMenu.qml")); // Requires a root menu to have been loaded before it can load - HifiMenu::load(); - HifiMenu::connectAction(MenuConstants::Quit, qApp, SLOT(quit())); + VrMenu::load(); #endif installEventFilter(offscreenUi.data()); offscreenUi->resume(); @@ -407,10 +406,6 @@ protected: switch (event->key()) { case Qt::Key_L: if (event->modifiers() & Qt::CTRL) { - auto offscreenUi = DependencyManager::get(); - HifiMenu * menu = offscreenUi->findChild(); - menu->addItem("", "Test 3"); - menu->addItem("File", "Test 3"); } break; case Qt::Key_K: @@ -433,7 +428,7 @@ protected: QQmlContext* menuContext{ nullptr }; void keyReleaseEvent(QKeyEvent *event) { if (_altPressed && Qt::Key_Alt == event->key()) { - HifiMenu::toggle(); + VrMenu::toggle(); } }