238 lines · 8.3 KB · C++
RawDownload
////  VrMenu.cpp////  Created by Bradley Austin Davis on 2015/04/21//  Copyright 2015 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "VrMenu.h" #include <QtQml>#include <QMenuBar> #include "OffscreenUi.h" static unsigned int USER_DATA_ID = 0; // Binds together a Qt Action or Menu with the QML Menu or MenuItem//// TODO On reflection, it may be pointless to use the UUID.  Perhaps// simply creating the bidirectional link pointing to both the widget// and qml object and inject the pointer into both objectsclass MenuUserData : public QObjectUserData {public:    MenuUserData(QAction* action, QObject* qmlObject) {        if (!USER_DATA_ID) {            USER_DATA_ID = DependencyManager::get<OffscreenUi>()->getMenuUserDataId();        }        _action = action;        _qml = qmlObject;        action->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        updateQmlItemFromAction();        auto connection = QObject::connect(action, &QAction::changed, [=] {            updateQmlItemFromAction();        });        QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] {            QObject::disconnect(connection);        });    }     ~MenuUserData() {        _action->setUserData(USER_DATA_ID, nullptr);        _qml->setUserData(USER_DATA_ID, nullptr);    }     void updateQmlItemFromAction() {        _qml->setProperty("checkable", _action->isCheckable());        _qml->setProperty("enabled", _action->isEnabled());        QString text = _action->text();        _qml->setProperty("text", text);        _qml->setProperty("shortcut", _action->shortcut().toString());        _qml->setProperty("checked", _action->isChecked());        _qml->setProperty("visible", _action->isVisible());    }      const QUuid uuid{ QUuid::createUuid() };     static bool hasData(QAction* object) {        if (!object) {            qWarning() << "Attempted to fetch MenuUserData for null object";            return false;        }        return (nullptr != static_cast<MenuUserData*>(object->userData(USER_DATA_ID)));    }     static MenuUserData* forObject(QAction* object) {        if (!object) {            qWarning() << "Attempted to fetch MenuUserData for null object";            return nullptr;        }        auto result = static_cast<MenuUserData*>(object->userData(USER_DATA_ID));        if (!result) {            qWarning() << "Unable to find MenuUserData for object " << object;            if (auto action = dynamic_cast<QAction*>(object)) {                qWarning() << action->text();            } else if (auto menu = dynamic_cast<QMenu*>(object)) {                qWarning() << menu->title();            }            return nullptr;        }        return result;    } private:    MenuUserData(const MenuUserData&);     QAction* _action { nullptr };    QObject* _qml { nullptr };};  VrMenu::VrMenu(OffscreenUi* parent) : QObject(parent) {    _rootMenu = parent->getRootItem()->findChild<QObject*>("rootMenu");    parent->getRootContext()->setContextProperty("rootMenu", _rootMenu);} QObject* VrMenu::findMenuObject(const QString& menuOption) {    if (menuOption.isEmpty()) {        return _rootMenu;    }    QObject* result = _rootMenu->findChild<QObject*>(menuOption);    return result;}  void VrMenu::addMenu(QMenu* menu) {    Q_ASSERT(!MenuUserData::hasData(menu->menuAction()));    QObject* parent = menu->parent();    QObject* qmlParent = nullptr;    QMenu* parentMenu = dynamic_cast<QMenu*>(parent);    if (parentMenu) {        MenuUserData* userData = MenuUserData::forObject(parentMenu->menuAction());        if (!userData) {            return;        }        qmlParent = findMenuObject(userData->uuid.toString());    } else if (dynamic_cast<QMenuBar*>(parent)) {        qmlParent = _rootMenu;    } else {        Q_ASSERT(false);    }    QVariant returnedValue;    bool invokeResult = QMetaObject::invokeMethod(qmlParent, "addMenu", Qt::DirectConnection,                                                  Q_RETURN_ARG(QVariant, returnedValue),                                                  Q_ARG(QVariant, QVariant::fromValue(menu->title())));    Q_ASSERT(invokeResult);    Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x    QObject* result = returnedValue.value<QObject*>();    Q_ASSERT(result);    if (!result) {        qWarning() << "Unable to create QML menu for widget menu: " << menu->title();        return;    }     // Bind the QML and Widget together    new MenuUserData(menu->menuAction(), result);} void bindActionToQmlAction(QObject* qmlAction, QAction* action) {    auto text = action->text();    if (text == "Login") {        qDebug() << "Login action " << action;    }     new MenuUserData(action, qmlAction);    QObject::connect(action, &QAction::toggled, [=](bool checked) {        qmlAction->setProperty("checked", checked);    });    QObject::connect(qmlAction, SIGNAL(triggered()), action, SLOT(trigger()));} class QQuickMenuItem; void VrMenu::addAction(QMenu* menu, QAction* action) {    Q_ASSERT(!MenuUserData::hasData(action));     Q_ASSERT(MenuUserData::hasData(menu->menuAction()));    MenuUserData* userData = MenuUserData::forObject(menu->menuAction());    if (!userData) {        return;    }    QObject* menuQml = findMenuObject(userData->uuid.toString());    Q_ASSERT(menuQml);    QQuickMenuItem* returnedValue { nullptr };        bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection,        Q_RETURN_ARG(QQuickMenuItem*, returnedValue),        Q_ARG(QString, action->text()));     Q_ASSERT(invokeResult);    Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x    QObject* result = reinterpret_cast<QObject*>(returnedValue); // returnedValue.value<QObject*>();    Q_ASSERT(result);    // Bind the QML and Widget together    bindActionToQmlAction(result, action);} void VrMenu::addSeparator(QMenu* menu) {    Q_ASSERT(MenuUserData::hasData(menu->menuAction()));    MenuUserData* userData = MenuUserData::forObject(menu->menuAction());    if (!userData) {        return;    }    QObject* menuQml = findMenuObject(userData->uuid.toString());    Q_ASSERT(menuQml);     bool invokeResult = QMetaObject::invokeMethod(menuQml, "addSeparator", Qt::DirectConnection);    Q_ASSERT(invokeResult);    Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x} void VrMenu::insertAction(QAction* before, QAction* action) {    QObject* beforeQml{ nullptr };    {        MenuUserData* beforeUserData = MenuUserData::forObject(before);        Q_ASSERT(beforeUserData);        if (!beforeUserData) {            return;        }        beforeQml = findMenuObject(beforeUserData->uuid.toString());    }    QObject* menu = beforeQml->parent();    QQuickMenuItem* returnedValue { nullptr };    // FIXME this needs to find the index of the beforeQml item and call insertItem(int, object)    bool invokeResult = QMetaObject::invokeMethod(menu, "addItem", Qt::DirectConnection,        Q_RETURN_ARG(QQuickMenuItem*, returnedValue),        Q_ARG(QString, action->text()));    Q_ASSERT(invokeResult);    QObject* result = reinterpret_cast<QObject*>(returnedValue); // returnedValue.value<QObject*>();    Q_ASSERT(result);    bindActionToQmlAction(result, action);} class QQuickMenuBase; void VrMenu::removeAction(QAction* action) {    if (!action) {        qWarning("Attempted to remove invalid menu action");        return;    }    MenuUserData* userData = MenuUserData::forObject(action);    if (!userData) {        qWarning("Attempted to remove menu action with no found QML object");        return;    }        QObject* item = findMenuObject(userData->uuid.toString());    QObject* menu = item->parent();    // Proxy QuickItem requests through the QML layer    QQuickMenuBase* qmlItem = reinterpret_cast<QQuickMenuBase*>(item);    QMetaObject::invokeMethod(menu, "removeItem", Qt::DirectConnection, Q_ARG(QQuickMenuBase*, qmlItem));}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t