From cbbe03cee7277d241fc4194a910145cd6dd9c8e1 Mon Sep 17 00:00:00 2001 From: beholder Date: Fri, 25 Aug 2017 02:32:03 +0300 Subject: [PATCH] 6788: You can deselect your current view mode --- interface/resources/qml/desktop/Desktop.qml | 19 ++++++- .../qml/hifi/tablet/TabletMenuStack.qml | 44 ++++++++++++++-- interface/src/Menu.cpp | 22 ++++++-- libraries/ui/src/VrMenu.cpp | 50 ++++++++++++++++--- 4 files changed, 120 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index ca7226a6ab..ec0bb7245b 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -50,7 +50,24 @@ FocusScope { property bool desktopRoot: true // The VR version of the primary menu - property var rootMenu: Menu { objectName: "rootMenu" } + property var rootMenu: Menu { + objectName: "rootMenu" + + // for some reasons it is not possible to use just '({})' here as it gets empty when passed to TableRoot/DesktopRoot + property var exclusionGroupsByMenuItem : ListModel {} + + function addExclusionGroup(menuItem, exclusionGroup) + { + console.debug('entering Desktop::addExclusionGroup: menuItem = ', menuItem, 'exclusionGroup = ', exclusionGroup); + exclusionGroupsByMenuItem.append( + { + 'menuItem' : menuItem.toString(), + 'exclusionGroup' : exclusionGroup.toString() + } + ); + console.debug('Desktop::addExclusionGroup: ', exclusionGroup, 'rootMenu: ', this); + } + } // FIXME: Alpha gradients display as fuschia under QtQuick 2.5 on OSX/AMD // because shaders are 4.2, and do not include #version declarations. diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index 9076cd6c48..c088088a1c 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -64,8 +64,10 @@ Item { d.pop(); } - function toModel(items) { + function toModel(items, newMenu) { var result = modelMaker.createObject(tabletMenu); + var exclusionGroups = {}; + for (var i = 0; i < items.length; ++i) { var item = items[i]; if (!item.visible) continue; @@ -77,6 +79,28 @@ Item { if (item.text !== "Users Online") { result.append({"name": item.text, "item": item}) } + + for(var j = 0; j < tabletMenu.rootMenu.exclusionGroupsByMenuItem.count; ++j) + { + var entry = tabletMenu.rootMenu.exclusionGroupsByMenuItem.get(j); + if(entry.menuItem == item.toString()) + { + var exclusionGroupId = entry.exclusionGroup; + console.debug('item exclusionGroupId: ', exclusionGroupId) + + if(!exclusionGroups[exclusionGroupId]) + { + exclusionGroups[exclusionGroupId] = exclusiveGroupMaker.createObject(newMenu); + console.debug('new exclusion group created: ', exclusionGroups[exclusionGroupId]) + } + + var exclusionGroup = exclusionGroups[exclusionGroupId]; + + item.exclusiveGroup = exclusionGroup + console.debug('item.exclusiveGroup: ', item.exclusiveGroup) + } + } + break; case MenuItemType.Separator: result.append({"name": "", "item": item}) @@ -133,10 +157,24 @@ Item { } } + property Component exclusiveGroupMaker: Component { + ExclusiveGroup { + Component.onDestruction: { + console.debug('ExclusionGroup destroyed: ', this) + } + } + } + function buildMenu(items) { - var model = toModel(items); // Menus must be childed to desktop for Z-ordering - var newMenu = menuViewMaker.createObject(tabletMenu, { model: model, isSubMenu: topMenu !== null }); + var newMenu = menuViewMaker.createObject(tabletMenu); + console.debug('newMenu created: ', newMenu) + + var model = toModel(items, newMenu); + + newMenu.model = model; + newMenu.isSubMenu = topMenu !== null; + pushMenu(newMenu); return newMenu; } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2c4a515736..17cf782b70 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -56,6 +56,8 @@ Menu* Menu::getInstance() { return dynamic_cast(qApp->getWindow()->menuBar()); } +const char* exclusionGroupKey = "exclusionGroup"; + Menu::Menu() { auto dialogsManager = DependencyManager::get(); auto accountManager = DependencyManager::get(); @@ -222,32 +224,42 @@ Menu::Menu() { cameraModeGroup->setExclusive(true); // View > First Person - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( + auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( viewMenu, MenuOption::FirstPerson, Qt::CTRL | Qt::Key_F, true, qApp, SLOT(cameraMenuChanged()))); + firstPersonAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup)); + // View > Third Person - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( + auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( viewMenu, MenuOption::ThirdPerson, Qt::CTRL | Qt::Key_G, false, qApp, SLOT(cameraMenuChanged()))); + thirdPersonAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup)); + // View > Mirror - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( + auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( viewMenu, MenuOption::FullscreenMirror, Qt::CTRL | Qt::Key_H, false, qApp, SLOT(cameraMenuChanged()))); + viewMirrorAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup)); + // View > Independent [advanced] - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, + auto viewIndependentAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::IndependentMode, 0, false, qApp, SLOT(cameraMenuChanged()), UNSPECIFIED_POSITION, "Advanced")); + viewIndependentAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup)); + // View > Entity Camera [advanced] - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, + auto viewEntityCameraAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CameraEntityMode, 0, false, qApp, SLOT(cameraMenuChanged()), UNSPECIFIED_POSITION, "Advanced")); + viewEntityCameraAction->setProperty(exclusionGroupKey, QVariant::fromValue(cameraModeGroup)); + viewMenu->addSeparator(); // View > Center Player In View diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 3959e950e9..b81f259b66 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -26,12 +26,14 @@ static unsigned int USER_DATA_ID = 0; // and qml object and inject the pointer into both objects class MenuUserData : public QObjectUserData { public: - MenuUserData(QAction* action, QObject* qmlObject) { + MenuUserData(QAction* action, QObject* qmlObject, QObject* qmlParent) { if (!USER_DATA_ID) { USER_DATA_ID = DependencyManager::get()->getMenuUserDataId(); } _action = action; _qml = qmlObject; + _qmlParent = qmlParent; + action->setUserData(USER_DATA_ID, this); qmlObject->setUserData(USER_DATA_ID, this); qmlObject->setObjectName(uuid.toString()); @@ -43,6 +45,41 @@ public: _shutdownConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] { QObject::disconnect(_changedConnection); }); + + class ExclusionGroupSetter : public QObject { + public: + ExclusionGroupSetter(QObject* from, QObject* to, QObject* qmlParent) : QObject(from), _from(from), _to(to), _qmlParent(qmlParent) { + _from->installEventFilter(this); + } + + ~ExclusionGroupSetter() { + _from->removeEventFilter(this); + } + protected: + bool eventFilter(QObject* o, QEvent* e) { + if (e->type() == QEvent::DynamicPropertyChange) { + QDynamicPropertyChangeEvent* dpc = static_cast(e); + if (dpc->propertyName() == "exclusionGroup") + { + // unfortunately Qt doesn't support passing dynamic properties between C++ / QML, so we have to use this ugly helper function + QMetaObject::invokeMethod(_qmlParent, + "addExclusionGroup", + Qt::DirectConnection, + Q_ARG(QVariant, QVariant::fromValue(_to)), + Q_ARG(QVariant, _from->property(dpc->propertyName()))); + } + } + + return QObject::eventFilter(o, e); + } + + private: + QObject* _from; + QObject* _to; + QObject* _qmlParent; + }; + + new ExclusionGroupSetter(action, qmlObject, qmlParent); } ~MenuUserData() { @@ -110,6 +147,7 @@ private: QMetaObject::Connection _changedConnection; QAction* _action { nullptr }; QObject* _qml { nullptr }; + QObject* _qmlParent{ nullptr }; }; @@ -157,16 +195,16 @@ void VrMenu::addMenu(QMenu* menu) { } // Bind the QML and Widget together - new MenuUserData(menu->menuAction(), result); + new MenuUserData(menu->menuAction(), result, qmlParent); } -void bindActionToQmlAction(QObject* qmlAction, QAction* action) { +void bindActionToQmlAction(QObject* qmlAction, QAction* action, QObject* qmlParent) { auto text = action->text(); if (text == "Login") { qDebug(uiLogging) << "Login action " << action; } - new MenuUserData(action, qmlAction); + new MenuUserData(action, qmlAction, qmlParent); QObject::connect(action, &QAction::toggled, [=](bool checked) { qmlAction->setProperty("checked", checked); }); @@ -195,7 +233,7 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { QObject* result = reinterpret_cast(returnedValue); // returnedValue.value(); Q_ASSERT(result); // Bind the QML and Widget together - bindActionToQmlAction(result, action); + bindActionToQmlAction(result, action, _rootMenu); } void VrMenu::addSeparator(QMenu* menu) { @@ -231,7 +269,7 @@ void VrMenu::insertAction(QAction* before, QAction* action) { Q_ASSERT(invokeResult); QObject* result = reinterpret_cast(returnedValue); // returnedValue.value(); Q_ASSERT(result); - bindActionToQmlAction(result, action); + bindActionToQmlAction(result, action, _rootMenu); } class QQuickMenuBase;