Switching to a wrapper mechanism for VR menus

This commit is contained in:
Brad Davis 2015-04-25 16:20:15 -07:00
parent ee30588fd4
commit a96f69a673
30 changed files with 1335 additions and 1045 deletions

View file

@ -48,7 +48,7 @@ Dialog {
helperText: "domain, location, @user, /x,y,z"
anchors.margins: 8
onAccepted: {
event.accepted
event.accepted
addressBarDialog.loadAddress(addressLine.text)
}
}

View file

@ -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"

View file

@ -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"
}
}
}
}

View file

@ -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

View file

@ -8,7 +8,7 @@ Root {
anchors.fill: parent
onParentChanged: {
forceActiveFocus();
forceActiveFocus();
}
}

View file

@ -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")
}
}

View file

@ -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);
}

View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

View file

@ -52,8 +52,6 @@
#include <QMimeData>
#include <QMessageBox>
#include <QJsonDocument>
#include <QQmlNetworkAccessManagerFactory>
#include <QThreadStorage>
#include <AddressManager.h>
#include <AccountManager.h>
@ -65,6 +63,7 @@
#include <GlowEffect.h>
#include <HFActionEvent.h>
#include <HFBackEvent.h>
#include <HifiMenu.h>
#include <LogHandler.h>
#include <MainWindow.h>
#include <ModelEntityItem.h>
@ -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>();
offscreenUi->create(_glWidget->context()->contextHandle());
@ -902,7 +904,9 @@ void Application::runTests() {
}
void Application::audioMuteToggled() {
Menu::getInstance()->setIsOptionChecked(MenuOption::MuteAudio, DependencyManager::get<AudioClient>()->isMuted());
QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteAudio);
Q_CHECK_PTR(muteAction);
muteAction->setChecked(DependencyManager::get<AudioClient>()->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();

View file

@ -623,6 +623,9 @@ private:
void checkSkeleton();
QWidget* _fullscreenMenuWidget = new QWidget();
int _menuBarHeight;
QHash<QString, AcceptURLMethod> _acceptedExtensions;
QList<QString> _domainConnectionRefusals;

View file

@ -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<QAction*>(sender());
QString address = action->data().toString();
DependencyManager::get<AddressManager>()->handleLookupString(address);
}
void Bookmarks::deleteBookmark() {
QStringList bookmarkList;
bookmarkList.append(_bookmarks.keys());
QList<QAction*> 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<AddressManager>()->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);
}

View file

@ -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<MenuWrapper> _bookmarksMenu;
QPointer<QAction> _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

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -18,77 +18,114 @@
#include <QKeySequence>
#include <QPointer>
#include <QStandardPaths>
#include <QQuickItem>
#include <MenuItemProperties.h>
#include <OffscreenUi.h>
#include "DiscoverabilityManager.h"
#include <HifiMenu.h>
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()>);
void setToggleAction(std::function<void(bool)>);
QList<QAction*> 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<QMenu*, MenuWrapper*> _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<Menu>("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<void()> 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<QString, QAction*> _actionHash;
};
namespace MenuOption {
@ -243,4 +280,5 @@ namespace MenuOption {
const QString VisibleToNoOne = "No one";
const QString Wireframe = "Wireframe";
}
#endif // hifi_Menu_h

View file

@ -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);
}
}

View file

@ -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));
}

View file

@ -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();

View file

@ -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 <gpu/GPUConfig.h>
#include <QFormLayout>
#include <QGuiApplication>

View file

@ -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();
});
}

View file

@ -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);

View file

@ -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);

View file

@ -1,47 +1,64 @@
#include "HifiMenu.h"
#include <QtQml>
#include <QMenuBar>
// 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<MenuUserData*>(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<OffscreenUi>();
QObject * rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("rootMenu");
Q_ASSERT(rootMenu);
static_cast<HifiMenu*>(newItem)->setRootMenu(rootMenu);
static_cast<VrMenu*>(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<void(bool)> f) {
_toggleActions[name] = f;
}
void HifiMenu::setTriggerAction(const QString & name, std::function<void()> 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<QObject*>();
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<QObject*>(menuOption + MENU_SUFFIX);
const QObject* result = _rootMenu->findChild<QObject*>(menuOption);
return result;
}
QObject* HifiMenu::findMenuObject(const QString & menuOption) {
QObject* VrMenu::findMenuObject(const QString & menuOption) {
if (menuOption.isEmpty()) {
return _rootMenu;
}
QObject* result = _rootMenu->findChild<QObject*>(menuOption + MENU_SUFFIX);
QObject* result = _rootMenu->findChild<QObject*>(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<void()> 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<QString, QString> 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<QMenu*>(parent)) {
MenuUserData* userData = MenuUserData::forObject(parent);
qmlParent = findMenuObject(userData->uuid.toString());
} else if (dynamic_cast<QMenuBar*>(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<void(bool)> 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<OffscreenUi>();
QObject* group = offscreenUi->getRootItem()->findChild<QObject*>(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<OffscreenUi>();
QObject* rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("AllActions");
QString name = "HifiAction_" + QVariant(action).toString();
QObject* quitAction = rootMenu->findChild<QObject*>(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<QVariant> 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<QObject*>();
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<QObject*>(returnedValue);
}
Q_ASSERT(result);
bindActionToQmlAction(result, action);
}
void VrMenu::removeAction(QAction* action) {
// FIXME implement
}

View file

@ -16,67 +16,32 @@
#include <QHash>
#include <QList>
#include <QSignalMapper>
#include <QAction>
#include <QMenu>
#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<void(bool)> f);
void setTriggerAction(const QString& name, std::function<void()> 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<void()> 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<void(bool)> 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<QString, std::function<void()>> _triggerActions;
QHash<QString, std::function<void(bool)>> _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

View file

@ -366,10 +366,14 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(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::function<voi
item = _rootItem->findChild<QQuickItem*>(name);
}
if (item) {
qDebug() << "Turning item " << !item->isEnabled();
item->setEnabled(!item->isEnabled());
}
}

View file

@ -320,7 +320,7 @@ public:
glDisable(GL_DEPTH_TEST);
MessageDialog::registerType();
HifiMenu::registerType();
VrMenu::registerType();
qmlRegisterType<MenuConstants>("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<OffscreenUi>();
HifiMenu * menu = offscreenUi->findChild<HifiMenu*>();
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();
}
}