Fix menu items created by plugins

This commit is contained in:
Brad Davis 2016-04-07 19:17:16 -07:00
parent 3349093908
commit 4b02e8437c
5 changed files with 107 additions and 90 deletions

View file

@ -338,6 +338,13 @@ QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const Q
return waitForInputDialogResult(createInputDialog(icon, title, label, current));
}
void OffscreenUi::addMenuInitializer(std::function<void(VrMenu*)> f) {
if (!_vrMenu) {
_queuedMenuInitializers.push_back(f);
return;
}
f(_vrMenu);
}
QQuickItem* OffscreenUi::createInputDialog(const Icon icon, const QString& title, const QString& label,
const QVariant& current) {
@ -445,7 +452,10 @@ void OffscreenUi::createDesktop(const QUrl& url) {
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
new VrMenu(this);
_vrMenu = new VrMenu(this);
for (const auto& menuInitializer : _queuedMenuInitializers) {
menuInitializer(_vrMenu);
}
new KeyboardFocusHack();
@ -598,5 +608,9 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) {
return result;
}
unsigned int OffscreenUi::getMenuUserDataId() const {
return _vrMenu->_userDataId;
}
#include "OffscreenUi.moc"

View file

@ -13,7 +13,10 @@
#define hifi_OffscreenUi_h
#include <unordered_map>
#include <functional>
#include <QtCore/QVariant>
#include <QtCore/QQueue>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QInputDialog>
@ -23,10 +26,12 @@
#include "OffscreenQmlElement.h"
class VrMenu;
class OffscreenUi : public OffscreenQmlSurface, public Dependency {
Q_OBJECT
friend class VrMenu;
public:
OffscreenUi();
virtual void create(QOpenGLContext* context) override;
@ -39,6 +44,7 @@ public:
void unfocusWindows();
void toggleMenu(const QPoint& screenCoordinates);
bool eventFilter(QObject* originalDestination, QEvent* event) override;
void addMenuInitializer(std::function<void(VrMenu*)> f);
QQuickItem* getDesktop();
QQuickItem* getToolWindow();
@ -125,6 +131,8 @@ public:
static QString getText(const Icon icon, const QString & title, const QString & label, const QString & text = QString(), bool * ok = 0);
static QString getItem(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0);
unsigned int getMenuUserDataId() const;
signals:
void showDesktop();
@ -134,6 +142,8 @@ private:
QQuickItem* _desktop { nullptr };
QQuickItem* _toolWindow { nullptr };
std::unordered_map<int, bool> _pressedKeys;
VrMenu* _vrMenu { nullptr };
QQueue<std::function<void(VrMenu*)>> _queuedMenuInitializers;
};
#endif

View file

@ -9,38 +9,71 @@
//
#include "VrMenu.h"
#include <QtQml>
#include <QMenuBar>
#include "OffscreenUi.h"
unsigned int USER_DATA_ID() {
return DependencyManager::get<OffscreenUi>()->getMenuUserDataId();
}
// 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 objects
class MenuUserData : public QObjectUserData {
static const int USER_DATA_ID;
public:
MenuUserData(QAction* action, QObject* qmlObject) {
init(action, qmlObject);
}
MenuUserData(QMenu* menu, QObject* qmlObject) {
init(menu, qmlObject);
_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() {
_widget->setUserData(USER_DATA_ID, nullptr);
_qml->setUserData(USER_DATA_ID, nullptr);
_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 MenuUserData* forObject(QObject* object) {
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));
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)) {
@ -55,46 +88,15 @@ public:
private:
MenuUserData(const MenuUserData&);
void init(QObject* widgetObject, QObject* qmlObject) {
_widget = widgetObject;
_qml = 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()));
}
QObject* _widget { nullptr };
QAction* _action { nullptr };
QObject* _qml { nullptr };
};
const int MenuUserData::USER_DATA_ID = QObject::registerUserData();
VrMenu* VrMenu::_instance{ nullptr };
static QQueue<std::function<void(VrMenu*)>> queuedLambdas;
void VrMenu::executeOrQueue(std::function<void(VrMenu*)> f) {
if (_instance) {
foreach(std::function<void(VrMenu*)> priorLambda, queuedLambdas) {
priorLambda(_instance);
}
f(_instance);
} else {
queuedLambdas.push_back(f);
}
}
VrMenu::VrMenu(QObject* parent) : QObject(parent) {
_instance = this;
auto offscreenUi = DependencyManager::get<OffscreenUi>();
_rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("rootMenu");
offscreenUi->getRootContext()->setContextProperty("rootMenu", _rootMenu);
foreach(std::function<void(VrMenu*)> f, queuedLambdas) {
f(this);
}
queuedLambdas.clear();
VrMenu::VrMenu(OffscreenUi* parent) : QObject(parent) {
_rootMenu = parent->getRootItem()->findChild<QObject*>("rootMenu");
parent->getRootContext()->setContextProperty("rootMenu", _rootMenu);
}
QObject* VrMenu::findMenuObject(const QString& menuOption) {
@ -105,21 +107,14 @@ QObject* VrMenu::findMenuObject(const QString& menuOption) {
return result;
}
void updateQmlItemFromAction(QObject* target, QAction* source) {
target->setProperty("checkable", source->isCheckable());
target->setProperty("enabled", source->isEnabled());
target->setProperty("text", source->text());
target->setProperty("shortcut", source->shortcut().toString());
target->setProperty("checked", source->isChecked());
target->setProperty("visible", source->isVisible());
}
void VrMenu::addMenu(QMenu* menu) {
Q_ASSERT(!MenuUserData::forObject(menu));
Q_ASSERT(!MenuUserData::hasData(menu->menuAction()));
QObject* parent = menu->parent();
QObject* qmlParent = nullptr;
if (dynamic_cast<QMenu*>(parent)) {
MenuUserData* userData = MenuUserData::forObject(parent);
QMenu* parentMenu = dynamic_cast<QMenu*>(parent);
if (parentMenu) {
MenuUserData* userData = MenuUserData::forObject(parentMenu->menuAction());
if (!userData) {
return;
}
@ -143,28 +138,16 @@ void VrMenu::addMenu(QMenu* menu) {
}
// Bind the QML and Widget together
new MenuUserData(menu, result);
auto menuAction = menu->menuAction();
updateQmlItemFromAction(result, menuAction);
auto connection = QObject::connect(menuAction, &QAction::changed, [=] {
updateQmlItemFromAction(result, menuAction);
});
QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] {
QObject::disconnect(connection);
});
new MenuUserData(menu->menuAction(), result);
}
void bindActionToQmlAction(QObject* qmlAction, QAction* action) {
new MenuUserData(action, qmlAction);
updateQmlItemFromAction(qmlAction, action);
auto connection = QObject::connect(action, &QAction::changed, [=] {
updateQmlItemFromAction(qmlAction, action);
});
QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] {
QObject::disconnect(connection);
});
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);
});
@ -174,9 +157,10 @@ void bindActionToQmlAction(QObject* qmlAction, QAction* action) {
class QQuickMenuItem;
void VrMenu::addAction(QMenu* menu, QAction* action) {
Q_ASSERT(!MenuUserData::forObject(action));
Q_ASSERT(MenuUserData::forObject(menu));
MenuUserData* userData = MenuUserData::forObject(menu);
Q_ASSERT(!MenuUserData::hasData(action));
Q_ASSERT(MenuUserData::hasData(menu->menuAction()));
MenuUserData* userData = MenuUserData::forObject(menu->menuAction());
if (!userData) {
return;
}
@ -197,8 +181,8 @@ void VrMenu::addAction(QMenu* menu, QAction* action) {
}
void VrMenu::addSeparator(QMenu* menu) {
Q_ASSERT(MenuUserData::forObject(menu));
MenuUserData* userData = MenuUserData::forObject(menu);
Q_ASSERT(MenuUserData::hasData(menu->menuAction()));
MenuUserData* userData = MenuUserData::forObject(menu->menuAction());
if (!userData) {
return;
}

View file

@ -24,8 +24,7 @@
class VrMenu : public QObject {
Q_OBJECT
public:
static void executeOrQueue(std::function<void(VrMenu*)> f);
VrMenu(QObject* parent = nullptr);
VrMenu(OffscreenUi* parent = nullptr);
void addMenu(QMenu* menu);
void addAction(QMenu* parent, QAction* action);
void addSeparator(QMenu* parent);
@ -36,8 +35,9 @@ protected:
QObject* _rootMenu{ nullptr };
QObject* findMenuObject(const QString& name);
static VrMenu* _instance;
friend class MenuUserData;
friend class OffscreenUi;
const unsigned int _userDataId { QObject::registerUserData() };
};
#endif // hifi_VrMenu_h

View file

@ -15,6 +15,8 @@
#include <SettingHandle.h>
#include "../VrMenu.h"
#include "../OffscreenUi.h"
#include "Logging.h"
using namespace ui;
@ -495,7 +497,8 @@ void Menu::removeActionGroup(const QString& groupName) {
}
MenuWrapper::MenuWrapper(ui::Menu& rootMenu, QMenu* menu) : _rootMenu(rootMenu), _realMenu(menu) {
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->addMenuInitializer([=](VrMenu* vrMenu) {
vrMenu->addMenu(menu);
});
_rootMenu._backMap[menu] = this;
@ -515,7 +518,8 @@ void MenuWrapper::setEnabled(bool enabled) {
QAction* MenuWrapper::addSeparator() {
QAction* action = _realMenu->addSeparator();
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->addMenuInitializer([=](VrMenu* vrMenu) {
vrMenu->addSeparator(_realMenu);
});
return action;
@ -523,14 +527,16 @@ QAction* MenuWrapper::addSeparator() {
void MenuWrapper::addAction(QAction* action) {
_realMenu->addAction(action);
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->addMenuInitializer([=](VrMenu* vrMenu) {
vrMenu->addAction(_realMenu, action);
});
}
QAction* MenuWrapper::addAction(const QString& menuName) {
QAction* action = _realMenu->addAction(menuName);
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->addMenuInitializer([=](VrMenu* vrMenu) {
vrMenu->addAction(_realMenu, action);
});
return action;
@ -538,7 +544,8 @@ QAction* MenuWrapper::addAction(const QString& menuName) {
QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) {
QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut);
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->addMenuInitializer([=](VrMenu* vrMenu) {
vrMenu->addAction(_realMenu, action);
});
return action;
@ -546,14 +553,16 @@ QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver
void MenuWrapper::removeAction(QAction* action) {
_realMenu->removeAction(action);
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->addMenuInitializer([=](VrMenu* vrMenu) {
vrMenu->removeAction(action);
});
}
void MenuWrapper::insertAction(QAction* before, QAction* action) {
_realMenu->insertAction(before, action);
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->addMenuInitializer([=](VrMenu* vrMenu) {
vrMenu->insertAction(before, action);
});
}